I have an observable which might fail with a special exception in which case I want to show a dialog with a retry button. I've seen this answer, but it doesn't quite do what I want. I wasn't able to use retryWhen to solve my problem, so instead I used onErrorResumeNext. If you can come up with a way to do the same with retryWhen, please tell.
Right now I have this piece of code:
public Observable<Order> proceedWithOrdering(Activity activity) {
return apiService.createOrder()
.subscribeOn(Schedulers.io())
.compose(applyRetryLogic(activity))
.subscribeOn(AndroidSchedulers.mainThread())
.observeOn(AndroidSchedulers.mainThread());
}
public <T extends ApiResponse> Observable.Transformer<T, T> applyRetryLogic(Activity activity) {
return observable -> observable
.onErrorResumeNext(retry(observable, activity))
.subscribeOn(AndroidSchedulers.mainThread());
}
public <T> Func1<Throwable, ? extends Observable<? extends T>> retry(Observable toRetry, Activity activity) {
return throwable -> {
if (throwable instanceof NetworkException) {
MaterialDialog dialog = retryDialog(activity);
View retry = dialog.getActionButton(DialogAction.POSITIVE);
View cancel = dialog.getActionButton(DialogAction.NEGATIVE);
Observable<Object> retryClick = RxView.clicks(retry).map(o -> {
dialog.dismiss();
return o;
});
Observable<Object> cancelClick = RxView.clicks(cancel).flatMap(o -> {
dialog.dismiss();
return Observable.error(throwable);
});
dialog.show();
return Observable.amb(retryClick, cancelClick)
.flatMap(o -> toRetry.compose(applyRetryLogic(activity)));
} else {
return Observable.error(throwable);
}
};
}
The problem is that the call inside the retry gets executed not on the main thread and it raises the Can't create handler inside thread that has not called Looper.prepare() exception.
The question is - how do I force it to be executed on the main thread? As you can see, I have already tried doing subscribeOn(AndroidSchedulers.mainThread()) right after both compose and onErrorResumeNext with no luck.
I have tested my code using simple observables that don't operate on separate threads and it works fine.
You can accomplish this by flatMapping a PublishSubject which is then updated once the relevant button is pressed. Here is a classical Java Swing example.
public class RetryWhenEnter {
public static void main(String[] args) {
AtomicInteger d = new AtomicInteger();
Observable<Integer> source = Observable.just(1);
source.flatMap(v -> {
if (d.incrementAndGet() < 3) {
return Observable.error(new RuntimeException());
}
return Observable.just(v);
})
.retryWhen(err -> {
return err.flatMap(e -> {
System.out.println(Thread.currentThread() + " Error!");
PublishSubject<Integer> choice = PublishSubject.create();
SwingUtilities.invokeLater(() -> {
int c = JOptionPane.showConfirmDialog(null,
e.toString() + "\r\nRetry?", "Error",
JOptionPane.YES_NO_OPTION);
if (c == JOptionPane.YES_OPTION) {
choice.onNext(1);
} else {
choice.onCompleted();
}
});
return choice;
});
}).subscribe(System.out::println,
Throwable::printStackTrace);
}
}
Edit:
Or use observeOn(AndroidSchedulers.mainThread()) just before onErrorResumeNext or when using retryWhen: retryWhen(o -> o.observeOn(AndroidSchedulers.mainThread())...).
Edit 2
I've rolled back the change so the answer is meaningful again.
There is a way of solving my problem using retryWhen (thanks #akarnokd):
public <T extends ApiResponse> Observable.Transformer<T, T> applyRetryLogic(Activity activity) {
return observable -> observable
.retryWhen(err -> err.flatMap(throwable -> {
L.d(Thread.currentThread() + " Error!");
if (throwable instanceof NetworkException) {
PublishSubject<Integer> choice = PublishSubject.create();
activity.runOnUiThread(() -> {
MaterialDialog dialog = retryDialog(activity);
View retry = dialog.getActionButton(DialogAction.POSITIVE);
View cancel = dialog.getActionButton(DialogAction.NEGATIVE);
RxView.clicks(retry).subscribe(o -> {
dialog.dismiss();
choice.onNext(1);
});
RxView.clicks(cancel).subscribe(o -> {
dialog.dismiss();
choice.onError(throwable);
});
dialog.show();
});
return choice;
} else {
return Observable.error(throwable);
}
}));
}
Related
I looked the same topics and found nothing that could help me.
So this is my problem. I have an activity with a RecyclerView. I'm using Retrofit2 library to get list of news, here it is:
#GET(VERSION_V1 + "/news")
Observable<BaseResponse<NewsListResponse>> getCompanyNews(#Query("companyId") String companyId,
#Query("query") String query,
#Query("offset") Integer offset,
#Query("limit") Integer limit,
#Query("creator") NewsCreatorType creator,
#Query("date")NewsFilterType dateFilter);
And I have a Presenter:
public void loadData(boolean refresh) {
mCoreServices
.getApiService()
.getApi()
.getCompanyNews(mCompanyId, "", 0, 100, mCompanyId == null ? NewsCreatorType.SYSTEM : NewsCreatorType.COMPANY, mFilterType)
.map(response -> response.getResult().getItems())
.flatMap(news -> {
if (mCompanyId == null || "".equals(mCompanyId)) {
return Observable.just(news);
} else {
return mCoreServices
.getApiService()
.getApi()
.getCompanyNews(null, "", 0, 100, NewsCreatorType.SYSTEM, mFilterType)
.map(response -> {
news.addAll(response.getResult().getItems());
return news;
});
}
})
.flatMapIterable(newsItems -> newsItems)
.map(NewsViewModel::new)
.toSortedList((news1, news2) -> news2.getDate().compareTo(news1.getDate()))
.retryWhen(errors -> retryAfterRefreshToken(errors))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(() -> {
if (refresh) {
getViewState().onShowRefresh(true);
} else {
getViewState().onShowProgressBar(true);
}
})
.doOnUnsubscribe(() -> {
if (refresh) {
getViewState().onShowRefresh(false);
} else {
getViewState().onShowProgressBar(false);
}
})
.subscribe(news -> {
mResults = news;
getViewState().onLoadNews(mResults);
}, new ApiExceptionObservable(TAG) {
#Override
public void call(String message) {
getViewState().onShowErrorMessage(message);
}
#Override
public void unauthorized() {
getViewState().showUnauthorizedDialog();
}
});
}
And ofc I have a RecyclerView adapter, this is my onBindViewHolder:
#Override
public void onBindViewHolder(RecyclerView.ViewHolder baseHolder, int position) {
if (baseHolder instanceof ViewHolder) {
ViewHolder holder = (ViewHolder) baseHolder;
NewsViewModel newsViewModel = mResults.get(position);
holder.mTVTitle.setText(newsViewModel.getTitle());
holder.mTVContent.setText(newsViewModel.getContent());
if (NewsCreatorType.SYSTEM.equals(newsViewModel.getCreator())) {
holder.mTVCreator.setText(newsViewModel.getCreator().getTitle());
} else {
holder.mTVCreator.setText(
holder.mTVCreator.getContext().getString(
newsViewModel.getCreator().getTitle(),
mCompanyTitle
)
);
}
}
}
Well, I have 2 companies doing news. News from company#1 are not duplicated, news from company#2 are always duplicated.. I don't understand where's my mistake. I checked (using debugger) the answer on my GET, it has no duplicated news, so the problem with my adapter I guess..
Thank you.
EDIT:
I get duplicated news from Creator.SYSTEM only.
EDIT 2.0:
Okay, I removed .flatMap in my Presenter, now it works fine, but I can't understand why it was there, can someone explain please?
i would like to implement a Pollingservice which calls a REST Api every nDelay Seconds and notify all subscribers if the data has been changed. Now i have a little problem with my code since it always returns a value to my Consumer, even if the data has not been changed.
private Observable<List<HueLight>> pollingLightsObservable = null;
public Observable<List<HueLight>> getPollingLightsObservable() {
if (pollingLightsObservable == null) {
pollingLightsObservable = Observable.fromCallable(
() -> LightManager
.getInstance(context)
.getLights()
.blockingSingle())
// .distinctUntilChanged( (l1, l1) -> !l1.equals(l2) )
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1, TimeUnit.SECONDS)));
}
return pollingLightsObservable;
}
Enabling or using the distinctUntilChanged dont change anything. Doesnt matter if i put it before or after my repeatWhen.
Since my RetroFit Call returns an Observable, i have to use blockingSingle(). Using the Observable directly it leads into a return of "4, 8, 12, 16, .." items with this sample:
LightManager.getInstance(context).getLights()
.repeatWhen(o -> o.concatMap(v -> Observable.timer(1, TimeUnit.SECONDS)))
Currently i subscribe from different classes/activites with
this.lightChangeSubscriber = PollingManager
.getInstance(getContext())
.getPollingLightsObservable()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(hueLights -> {
{
Log.d(TAG, "Lights received successfully! Size=" + hueLights.size());
}
});
I would lovely avoid using interfaces and timer to create the polling. What would you recommend ?
what about using some custom filter?
public class FilterDuplicateHueConfig implements Predicate<HueConfig> {
private HueConfig lastVal;
#Override
public boolean test(HueConfig newVal) {
if(lastVal == null) {
lastVal = newVal;
return true;
}
... compare here the two values and return true/false appropriately...
}
}
I want to perform long calculations on background thread using RXJava in android. After calculation I am trying to present the result in Recylerview. I am using following piece of code:
Observable.just("true")
.subscribeOn(Schedulers.io())
.map(new Func1<String, String>() {
#Override
public String call(String s) {
feedlist.clear();
if (eventFeedItems != null && !eventFeedItems.isEmpty()) {
for (int i = 0; i < eventFeedItems.size(); i++) {
if (eventFeedItems != null && eventFeedItems.get(i) != null
&& ((eventFeedItems.get(i).getType() != null && eventFeedItems.get(i).getType().equalsIgnoreCase("EVENT"))
|| (eventFeedItems.get(i).getActivityRequestType() != null && eventFeedItems.get(i).getActivityRequestType().equalsIgnoreCase(EventConstants.TRENDING_ACTIVITY)))) {
if (eventFeedItems.get(i).getActivityRequestType() != null && !eventFeedItems.get(i).getActivityRequestType().equalsIgnoreCase("")) {
feedlist.add(new FeedsListModel(eventFeedItems.get(i), eventFeedItems.get(i).getActivityRequestType(), null));
} else if (eventFeedItems.get(i).getRequestType() != null && !eventFeedItems.get(i).getRequestType().equalsIgnoreCase("")) {
feedlist.add(new FeedsListModel(eventFeedItems.get(i), eventFeedItems.get(i).getRequestType(), null));
} else
feedlist.add(new FeedsListModel(eventFeedItems.get(i), EventConstants.ATTENDEE_POST, null));
}
}
}
Log.d("calculations","Completed");
return "";
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
#Override
public void call(String s) {
// feed_list.setLayoutManager(mLayoutManager);
// feedListAdapter.notifyDataSetChanged();
Log.d("Adapter", "Set");
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
Log.d("Exception", "oh! fish...");
throwable.printStackTrace();
}
});
With the above piece of code I am facing UI Hindring as the ArrayList eventFeedItems size is of about more then 300 items.I am new to RXJava. Please help me out.
you will not achieve concurrency using map-Operator.
The first subscribeOn will move all emitions to IO-scheduler. No concurrency happening here.
.subscribeOn(Schedulers.io())
The map-operator will be called synchronously from previous thread. In your case it would be some thread from IO-threadpool.
.map(new Func1<String, String>() {
After the map-Operator has been executed, you will move the value from the IO-thread to the Android-UI-event-loop with
.observeOn(AndroidSchedulers.mainThread())
After the value has been translated from IO-thread to the UI-thread the next value from the initial observable will be processed.
Observable.just("true")
In your example there will be no more values because you only produce one value.
In order to achieve concurrency you should use flatMap instead of map. And use subscribeOn() in the flatMap to create each stream on another thread.
Please consider this example, to see how concurrency is happening. Every observable will be subscribed at once, so the max. time for the teset would be something around 5 seconds. If now concurrency would happen it would take 1+2+3+4+5 seconds plus execution time.
#Test
public void name1() throws Exception {
Observable<Integer> value = Observable.just(1_000, 2_000, 3_000, 4_000, 5_000)
.flatMap(i -> Observable.fromCallable(() -> doWork(i)).subscribeOn(Schedulers.io())
).doOnNext(integer -> System.out.println("value"));
value.test().awaitTerminalEvent();
}
private int doWork(int sleepMilli) {
try {
Thread.sleep(sleepMilli);
} catch (InterruptedException e) {
e.printStackTrace();
}
return -1;
}
If you want to know more about how concurrency is happening with flatMap please consider reading http://tomstechnicalblog.blogspot.de/2015/11/rxjava-achieving-parallelization.html
In regard to your code I would suggest:
Move the anonymouse implementation of interface to a private inner class implementation and use an instance of it. You will get a more readable observable
Don't use side-effects to global variables from operators within. You
will get race-condition if concurrency is involved.
List<FeedsListModel> eventFeedItems = Arrays.asList(new FeedsListModel(), new FeedsListModel());
Observable<FeedsListModel> feedsListModelObservable = Observable.fromIterable(eventFeedItems)
.flatMap(feedsListModel -> Observable.fromCallable(() -> calculation(feedsListModel))
.subscribeOn(Schedulers.computation())
);
feedsListModelObservable
.toList()
.observeOn(Schedulers.io())
.subscribe(feedsListModels -> {
// do UI stuff
});
Helping-method:
private FeedsListModel calculation(FeedsListModel model) {
// do calculation here
return new FeedsListModel();
}
I am messing around some with the google awareness api and now my understanding of RxJava is limiting me.
What I want to achieve in the end:
I want to get a Weather and a Location from the Api, and merge them into one object that I can pass on to my view for update.
However, I'm not sure how I achieve the returning of an Observable from the api callback here since it has void return type, and how to achieve merging of the weather and location object from api.getWeather and api.getLocation
public void requestUserCurrentInfo() {
Subscription userInfo = getWeatherLocation().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(userinfo ->
Log.d(TAG,userinfo.something()));
}
public Observable<UserInfo> getWeatherLocation () {
try {
Awareness.SnapshotApi.getWeather(client)
.setResultCallback(weather -> {
if (!weather.getStatus().isSuccess()) {
Log.d(TAG, "Could not get weather");
return;
}
//How do I do here?
return weather.getWeather();
});
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(retrievedLocation -> {
if(!retrievedLocation.getStatus().isSuccess()) return;
Log.d("FRAG", retrievedLocation.getLocation().getLatitude() + "");
});
} catch (SecurityException exception) {
throw new SecurityException("No permission " + exception);
}
}
For my other things in my Project, I get some stuff through a REST api following the repository pattern, then I can get it like this because every step returns a Observable< SmhiResponse >
getWeatherSubscription = getWeatherUsecase.execute().subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(
smhiResponseModel -> {Log.d(TAG,"Retrieved weather"); locationView.hideLoading();},
err -> {Log.d(TAG,"Error fetching weather"); locationView.hideLoading();}
);
You don't return an observable from the callback but wrap your callbacks into observables to make them combinable (untested):
Observable<WeatherResult> weatherObservable = Observable.create(subscriber -> {
Awareness.SnapshotApi.getWeather(client)
.setResultCallback(weather -> {
if (!weather.getStatus().isSuccess()) {
subscriber.onError(new Exception("Could not get weather."));
Log.d(TAG, "Could not get weather");
} else {
//How do I do here?
subscriber.onNext(weather);
subscriber.onCompleted();
}
});
});
Observable<LocationResult> locationObservable = Observable.create(subscriber -> {
Awareness.SnapshotApi.getLocation(mGoogleApiClient)
.setResultCallback(retrievedLocation -> {
if(!retrievedLocation.getStatus().isSuccess()) {
subscriber.onError(new Exception("Could not get location."));
} else {
Log.d("FRAG", retrievedLocation.getLocation().getLatitude() + "");
subscriber.onNext(retrievedLocation);
subscriber.onCompleted();
}
});
});
now combine them via .combineLatest() or .zip():
Observable<CombinedResult> combinedResults = Observable.zip(weatherObservable, locationObservable,
(weather, location) -> {
/* somehow combine weather and location then return as type "CombinedResult" */
});
don't forget to subscribe, otherwise none of them gets executed:
combinedResults.subscribe(combinedResult -> {/*do something with that stuff...*/});
Observable.combineLatest(getWeather (), getLocation(), new Func2<List<Object_A>, List<Object_B>, Object>() {
#Override
public Object call(Object o, Object o2) {
combine both results and return the combine result to observer
}
})
getweather() and getlocation() return observables
i'm having trouble implementing rxJava in order to check if there is internet connection on android i'm doing it like this:
on my launcher activity i have this in onCreate:
AndroidObservable.bindActivity(this,
Observable.just(Utils.isActiveInternetConnection(Launcher.this)))
.subscribeOn(Schedulers.newThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});
i have an Utils class it is a final class with static methods the methods in that the observable is using are this ones:
public static boolean isActiveInternetConnection(Context context) {
if (isNetworkAvailable(context)) {
try {
HttpURLConnection urlc = (HttpURLConnection) (new URL("http://www.google.com").openConnection());
urlc.setRequestProperty("User-Agent", "Test");
urlc.setRequestProperty("Connection", "close");
urlc.setConnectTimeout(1500);
urlc.connect();
return (urlc.getResponseCode() == 200);
} catch (IOException e) {
Log.e("network", "Error checking internet connection", e);
}
} else {
Log.d("network", "No network available!");
}
return false;
}
private static boolean isNetworkAvailable(Context context){
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
if (null != activeNetwork) {
return true;
}
else {
return false;
}
}
i'm receiving android.os.NetworkOnMainThreadException and i can't find why, thanks in advance.
Observable.just(...) is called immediately on the calling thread (the main thread, in this case). Your code is effectively just an inlined version of this:
boolean activeConn = Utils.isActiveInternetConnection(Launcher.this);
AndroidObservable.bindActivity(this,
Observable.just(activeConn))
.subscribeOn(...)
...
You've tried to move it off the main thread by calling subscribeOn() - but the call has already happened.
The way we handle this (and I'm not sure that this is the best way, but it works) is to defer the network or blocking call until subscription happens, set up the observable to run on the correct threads, and then subscribe:
AndroidObservable.bindActivity(this,
Observable.defer(new Func0<Boolean>() {
#Override
public Observable<Observable<Boolean>> call() {
return Observable.just(Utils.isActiveInternetConnection(Launcher.this));
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Boolean>() {
#Override
public void call(Boolean aBoolean) {
if (aBoolean) {
Toast.makeText(Launcher.this, "There is internet connection", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(Launcher.this, "There is no internet connection", Toast.LENGTH_SHORT).show();
}
}
});
AdamS is correct, however RxJava 2 now offers Observable.fromCallable() to defer an observable operation till subscription.
A good reference:
https://caster.io/lessons/fromcallable-converting-slow-methods-into-an-observable/
Some example code from my use-case:
Single.fromCallable(new Callable<Response>() {
#Override
public Response call() throws Exception {
return NetworkGateway.networkRequest();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
I guess just just calls the method synchronously as it expects the boolean value and it tries to get it.
I am rather bad at RxJava but you may try something like this:
Observable<Boolean> onlineObservable = Observable.create(new Observable.OnSubscribe<Boolean>() {
#Override
public void call(Subscriber subscriber) {
subscriber.onNext(Utils.isActiveInternetConnection(context));
}
});
onlineObservable.subscribeOn(Schedulers.newThread()).subscribe(result -> {...});
this is my retrieve data from DataBase by RXAndroid code:
Observable.create(new Observable.OnSubscribe<List<GRO_VO>>() {
#Override
public void call(Subscriber<? super List<GRO_VO>> subscriber) {
String jsonIn;
jsonIn =retrieveDataFromDB();
Gson gson = new Gson();
Type listType = new TypeToken<List<GRO_VO>>() {
}.getType();
eventJoinList = gson.fromJson(jsonIn, listType);
Log.d("RX",jsonIn);
subscriber.onNext(eventJoinList);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<List<GRO_VO>>() {
#Override
public void call(List<GRO_VO> eventJoinList) {
Log.d("RX", ".subscribe");
recyclerView.setAdapter(new EventJoinAdapter(eventJoinList));
}
});
I think the just operator will emit data immediately, so it's is not useful to retrieve data from a database via network. It is very easy to use, but it can only be used for data that's already in the ram of the device.
I also had that problem, like #Baniares had, but after I use the create operator, all the problem gone...
From the RXJava documentation:
static <T> Observable<T> create(Observable.OnSubscribe<T> f)
Returns an Observable that will execute the specified function when a Subscriber subscribes to it.
Using the create operator can establish the standard process:
1 .subscribe(...) Subscriber(the sub class of class Observer) start the connection to Observable.
2 .subscribeOn(Schedulers.io()) collect a backGround Thread from the RX-ThreadPool
3 .create(...) retrieve data from server...during some netWork..etc
4 .observeOn(AndroidSchedulers.mainThread()) this means data will be set by the UI-Thread
5 Once we get the data , we can set the data in the onNext( ) method in the .subscribe( ) , the data will be set on the UI by UI-Thread since we make UI-thread do the work on the .observerOn(AndroidSchedulers.mainThread())
And note that if you use .create( ) operator,you must finished your observable in the .create( ) , others operator such like map,flatMap will not be executed after the .create( ) operator.
A very important concept we must need to know before start to use RXJava/RXAndroid. RX is a callback-based library, you tell RX what it should to do in what condition, it invoke your pass-in function(or in JAVA I may should to call them anonymous inner class... ) to achieve what you want.