I've got a chain of Observables and a dialog that is dismissing after everything is finished.The order is this:
1 api call get ResponseBody
2 take response body process (not ui thread)
3 other process (not ui thread)
During the first call the dialog is okay, when it comes to the second and I receive the body of the first call the dialog is blocked and it remain as is for the rest of the time.
At the end after everything is done, but I receive a warning says that "The app is doing to much work on the main thread".
I'm not doing anything on the main thread, so I don't really understand how i can unblock the dialog and keep everything on a separate thread.
showLoadingDialog();
mZappAppApis.downloadDatabase(Token.getToken(AppConfig.TOKEN_SYNC_DOWNLOAD_DATABASE))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<ResponseBody, Observable<String>>() {
#Override
public Observable<String> call(ResponseBody responseBody) {
return mDatabaseFileHelper.writeDatabaseToFile(responseBody);
}
})
.concatMap(new Func1<String, Observable<String>>() {
#Override
public Observable<String> call(String s) {
return mDatabaseFileHelper.copyDatabaseIntoZappApp();
}
})
.subscribe(new Subscriber<String>() {
#Override
public void onCompleted() {
dismissLoadingDialog();
saveLocalTimestamp(timestamp);
flowContinueInterface.onContinueFlow();
}
#Override
public void onError(Throwable e) {
Logger.e(e, "Error during processing new database");
dismissLoadingDialog();
flowContinueInterface.onStopFlow();
}
#Override
public void onNext(String result) {
Logger.d(result);
}
});
The concatMap work is happening on the main thread. You need to move the observeOn call to just above the subscribe call.
I would also move significant processing out of the subscriber into doOnCompleted and doOnError calls that are also placed before the observeOn.
Move your .observeOn(AndroidSchedulers.mainThread()) above the subscribe(… call.
Everything after your observeOn(… is executed on this thread.
You can see this in by printing out the current thread you are on:
.subscribeOn(Schedulers.newThread())
.concatMap(new Func1<String, Observable<String>>() {
#Override
public Observable<String> call(final String string) {
Log.i("Before", Thread.currentThread().toString());
return Observable.just(string);
}
})
.observeOn(AndroidSchedulers.mainThread())
.concatMap(new Func1<String, Observable<String>>() {
#Override
public Observable<String> call(final String string) {
Log.i("After", Thread.currentThread().toString());
return Observable.just(string);
}
})
...
Related
I use the following code to realize the countdown display at the millisecond level. After onSubscribe is executed, the onNext method is executed nearly one minute later.
This problem occurs occasionally. This problem also exists when creating Observable using the create method.
I don't have any solutions at present. Please help me, thank you
Observable.interval(1900, 50, TimeUnit.MILLISECONDS).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnDispose(() -> {
XLog.i("doOnDispose");
})
.compose(RxUtils.bindToLifecycle(provider))
.subscribe(new Observer<Long>() {
#Override
public void onSubscribe(#NonNull Disposable d) {
XLog.i("onSubscribe");
}
#Override
public void onNext(#NonNull Long aLong) {
XLog.i("onNext");
// refresh ui countdown
}
#Override
public void onError(#NonNull Throwable e) {
XLog.i("onError");
}
#Override
public void onComplete() {
XLog.i("onComplete");
}
});
I don't have any solutions at present
In the code below, the loadMoreStrings() method is have it's call() method execute before the getLabelsFromServer() completes. I'm still learning RxJava but I just can't get this to run properly.
private void fetchLabels() {
listObservable = Observable.fromCallable(new Callable<List<String>>() {
#Override
public List<String> call() {
return apiService.getLabelsFromServer();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
loadMoreStrings();
}
#Override
public void loadMoreStrings() {
stringListObservable.subscribe(new Action1<List<String>>() {
#Override
public void call(List<String> label) {
myStrings.addAll(label);
}
});
}
Because Observable type is lazy and it's not going to emit any items until you subscribe to it.
From code that you provided, you're not subscribing to listObservable. So it completes immediately and your loadMoreStrings() method is getting called.
I need to download a long list of 30k airports and put it on a offline database.
I made this code to download the json from the web:
bFetch.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(GithubService.SERVICE_ENDPOINT).build();
GithubService service = retrofit.create(GithubService.class);
service.getAirport()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Airport>>() {
#Override
public void onCompleted()
{
bClear.setText("OK");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Airport> airports)
{
Log.d("msh",String.valueOf(airports.size()));
}
});
}
});
and it works very well, but if I want to extract only one object, like map or a flatMap, it gives me this:
service.getAirport()
.map(new Func1<List<Airport>, Airport>()
{
#Override
public Airport call(List<Airport> airports) {
return null;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Airport>>() {
#Override
public void onCompleted()
{
bClear.setText("OK");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Airport> airports)
{
Log.d("msh",String.valueOf(airports.size()));
}
});
}
});
with the error:
Cannot resolve method 'subscribe(anonymous
rx.Subscriber>)
so:
what I have to do to solve it? My problem is that I don't understand very well rX and I have also a bit confusion
could I put data in realm database in map() method (if it works)?
Thank you
Since you're mapping from a List<Airport> to an Airport, you need to have a Subscriber<Airport> instead of Subscriber<List<Airport>>, along with the same change to the onNext method.
looks like it would compile with Java8 and RxJava2-RC5. I changed subscriber param from List to X and the onNext method from List to X. Maybe you coulde provide some more intel on your environment. Please notice that returning null is not possible anymore in RxJava2.
Furthermore notice that using newThread-Scheduler is not a good idea.
This scheduler simply starts a new thread every time it is requested
via subscri beOn() or observeOn() . newThread() is hardly ever a good
choice, not only because of the latency involved when starting a
thread, but also because this thread is not reused. --Tomasz Nurkiewicz from "Reactive Programming with RxJava"
Example-Impl with RxJava2-RC5
Observable.just(Arrays.asList("1", "2", "3"))
.map(new Function<List<String>, String>() {
#Override
public String apply(List<String> s) throws Exception {
return null;
}
}).subscribeOn(Schedulers.newThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String value) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
I'm using retrofit2 with Rx. I have two API calls. If first call returns empty response with code 400 I need to make second API call, if not then just to show result. I've implemented custom error handling how shown here. Here is my solution:
getResponse1(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(Response1 response) {
view.onSuccess(response);
}
#Override
public void onClientError(BaseError response) {
getResponse2(token)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response2, BaseError>(BaseError.class) {
#Override
public void onNext(Response2 response) {
view.onSuccess(response);
view.hideProgress();
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Is it possible to simplify this code that goes inside method onClientError? Is it good solution to like that?
1). To simplify it, it would be better that Response1 and Response2 will extend some base class, so in your chain, you can operate with base class, which can be casted to certain type when needed
So, let's assume, that you have BaseResponse:
public abstract class BaseResponse{
public static int TYPE_RESPONSE_1 = 1;
public static int TYPE_RESPONSE_2 = 2;
public abstract int getType(); //every class MUST override this method
}
Response1 and Response2 should override BaseResponse
2). getResponse1 and getResponse2 should return Observable<BaseResponse>
3). Your target code:
getResponse1(token)
.onErrorResumeNext(new Func1<Throwable, Observable<BaseResponse>>() {
#Override
public Observable<BaseResponse> call(Throwable throwable) {
// I use Retrofit 1.9
// And in Retrofit 1.9 I have class RetrofitError, which may provide me all info about error
// I'm absolutelly sure Retrofit 2 also have such class,
// but it may have another name
if(/* is your target error */){
//cast it tour target error
return getResponse2(token);
}
return Observable.error(throwable);
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ObserverErrorImpl<Response1, BaseError>(BaseError.class) {
#Override
public void onNext(BaseResponse response) {
if(response.getType() == BaseResponse.TYPE_RESPONSE_1){
view.onSuccess(response);
} else {
view.onSuccess(response);
view.hideProgress();
}
}
#Override
public void onError(Throwable throwable) {
super.onError(throwable);
view.hideProgress();
}
});
Jeesh, why does everyone make it so complicated? In practice I've felt that every time I needed to subscribe to an Observable in the subscribe of another, there is an operator that will do it much more cleanly for me:
<T,E extends Throwable> Observable<T>
whenExceptionIs(Class<E> what, Func1<E,Observable<T>> result) {
return t -> {
return what.isInstance(t) ? result.call(t) : Observable.error(t);
};
}
getResponse1(token)
.onErrorResumeNext(whenExceptionIs(BaseError.class, getResponse2(token)))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(view::onSuccess, view::hideProgress, err -> view.hideProgress());
If you have special error handling needs, create a custom Subscriber that handles that, but make sure that error handling in general is
handled in the Observable chain if it can do anything about it (ignore it, retry the calls etc)
propagated downstream.
I have the following code based on an example provided by #a.bertucci here Emit objects for drawing in the UI in a regular interval using RxJava on Android, where I zip an Observable with a Timer. When I trigger the subscription by calling processDelayedItems(), the code [A] in the zipped Observable is executed exactly once and one item is emitted to [B]. I would have expected code [A] to run continuously once triggered and keep emitting items every 1500 msec, but it obviously only runs once here.
private static void processDelayedItems() {
Observable.zip(
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
subscriber.OnNext(o)
}
}),
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS), new Func2<Object, Long, Object>() {
#Override public Object call(Object entity, Long aLong) {
return entity;
}
}
)
.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override public void call(Object entity) {
// ... and accordingly one item is emitted [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
throwable.printStackTrace();
}
}, new Action0() {
#Override public void call() {
}
});
}
Can anybody see the problem which I have here? Is it that I need to reference the Observable from outside the function to keep it alive for more time? Is it collected by GC (Android)? Is it a problem that the function is static?
What are the rules for Observables in terms of their livetime? Are there any best practices how longer-running Observables should be referenced and if they can be static at all? In my tests I noticed that it doesn't really matter, but maybe it does here, when a timer is involved.
--
Corrected code [not working yet]:
added repeat()
Observable.zip(
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
subscriber.OnNext(o);
subscriber.OnCompleted();
}
}).repeat(Schedulers.newThread()),
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS), new Func2<Object, Long, Object>() {
#Override public Object call(Object entity, Long aLong) {
return entity;
}
}
)
.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Object>() {
#Override public void call(Object entity) {
// ... and accordingly one item is emitted [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
throwable.printStackTrace();
}
}, new Action0() {
#Override public void call() {
}
});
You need repeat to generate an infinite Observable. E.g.,
Observable.create(new Observable.OnSubscribe<Object>() {
#Override public void call(Subscriber<? super Object> subscriber) {
// [A] this code is only called once
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(o);
}
if (!subscriber.isUnsubscribed()) {
subscriber.onCompleted();
}
}
}).repeat(Schedulers.newThread());
Is it that I need to reference the Observable from outside the function to keep it alive for more time? Is it collected by GC (Android)? Is it a problem that the function is static?
Since you use Schedulers.newThread() and timer, there will be some other Threads which has a reference to your Observable. You don't need more work.
What are the rules for Observables in terms of their livetime? Are there any best practices how longer-running Observables should be referenced and if they can be static at all? In my tests I noticed that it doesn't really matter, but maybe it does here, when a timer is involved.
You're right. It doesn't matter.
In regard to your comment, for simplicity you could do this,
Observable.timer(1500, 1500, TimeUnit.MILLISECONDS)
.flatMap(new Func1<Long, Observable<Object>>() {
#Override
public Observable<Object> call(Long aLong) {
String o = "0";
return Observable.from(o);
}
})
.subscribe(new Action1<Object>() {
#Override
public void call(Object aLong) {
System.out.println(aLong);
}
});
Here you still get the benefits of the timer without the added zip / repeat on top. It's still a bit verbose but it's a bit simpler.