I use Retrofit and RxJava for network calls. For the first time I ran into a weird problem. For one of the calls the following error message is displayed:
HTTP FAILED: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
But the call is started from the main thread and it's the same result even if I remove any calls in onNext. So it must be something in the call, which is a solo call! This is the call in a presenter:
public void updateEmail(final String newEmail) {
disposables.add(AccountRepository.updateEmail(newEmail)
.retry(1)
.subscribeWith(new DisposableObserver<Reply>() {
#Override
public void onNext(Reply reply) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
}));
}
And this is the Repository call:
public static Observable<Reply> updateEmail(String email) {
return getMyApiService().updateEmail(email)
.subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
.observeOn(AndroidSchedulers.mainThread());
}
And the Retrofit interface called MyApiInterface:
#FormUrlEncoded
#POST(UrlMap.apiProfileUpdateEmailUrl)
Observable<Reply> updateEmail(#Field("email") String email);
Nothing unusual, I have more than 100 calls like this.
Now if I add retry(1) as above, the call goes through, but the first call seem to be leaked, because it's still in the threads in the Network Profiler.
Furthermore, The call happens in a Fragment in an Activity with bottom navigation. If I switch to another fragment, all the calls there finish in the background without error, but the UI is not updated, I see a blank screen. But if I navigate to another activity and back, the UI is updating again.
The disposables is a CompositeDisposable object and it's cleared when the presenter is unbound. Disposable.clear in onNext() doesn't help.
I added five interceptors to the client for logging, headers, etc. Maybe they cause this somehow? Or something else? I'm trying to fix this for more than a day, but couldn't get much closer to the solution.
There is a listener for user verification changes -- that are coming from all the response headers -- in the main feed, so we can obfuscate the images. I wrapped the listener in a runOnUiThread() method and the leak went away.
Related
I'm learning RxJava so please be gentle. I've watched the tutorials, done the reading, searched SO, however, I'm still having some problems transforming my AsyncTaskLoader. For some reason, I can't find a pattern of operators to achieve my task (although I think it's a common one). What I'm trying to do is the following: return an Observable my fragment could subscribe to. The observable should do the following on subscribe:
1) Fetch data from the local database by doing 2 queries, running some logic and returning results;
2) Fetching data from API;
3) Synchronising the new API data with the database;
4) Repeating step one and returning results;
So far I've transformed my db calls and my API calls to return observables. I'm trying to understand how I can emit the cold results and continue with the chain. I could probably keep the two operations separately, and use the same subscriber to subscribe to both? But I'm not sure how that would work if my new loader-replacement class returns an observable... Also I don't really need to process the results from the second observable - I just need for the first one to replay when the second one finished.
So far I have the following:
public Observable<StuffFetchResult> getColdStuff() {
return Observable.zip(mDataSource.listStuff(), mDataSource.listOtherStuff(),
(stuff, moreStuff) -> {
List<Stuff> mergedList = new ArrayList<>();
// do some merging stuff
return new StuffFetchResult(mergedList);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Assume I also have getHotStuff() that will do the API call and the synchronisation with the database, if that's the right approach, and return the same Observable. However, I'm stuck on the next step - how can I restart the first observable to replay once hotStuff has completed, without adding another subscriber?
EDIT:
I've made some progress and I think all I need now is to join it all up. I have my two methods:
1) getColdStuff() is pretty much as described above
2) getHotStuff() will do call to the API, synchronise with the database, and return an Observable. The idea was to call getColdStuff() again after getHotStuff() has finished in order to refresh the UI, so actual result returned from getHotStuff() can be ignored. All it needs to do is to trigger getColdStuff() once done.
I've tried the suggestion in the answer to and created the following:
BehaviorRelay<Observable<StuffFetchResult>> callSequence = BehaviorRelay.create();
Observable<StuffFetchResult> valueSequence = Observable.switchOnNextDelayError(callSequence.toSerialized());
valueSequence.subscribe(new Subscriber<StuffFetchResult>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(StuffFetchResult result) {
// UI stuff
}
});
callSequence.call(loader.getColdStuff());
I can subscribe to valueSequence here and use callSequence.call(loader.getColdStuff());, which will run the first method and produce results in onNext() of my subscription, which I can use for my UI. However, I'm not sure how to run getHotStuff() in parallel and also do a different action on it when it returns. Also getHotStuff() returns a different type of Observable so I can't really use the same callSequence?
EDIT 2
Using two subscribers, I can achieve the required behaviour I think. Not really sure if that's the right way to go about it though.
loader.getHotStuff()
.subscribeOn(Schedulers.io())
.subscribe( new Subscriber<Object>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(Object stuffWeDontCareAbout) {
callSequence.call(loader.getColdStuff());
}
});
if i understand your scenario correctly, you may want something like that -
BehaviorSubject<Observable<T> callSequence = BehaviorSubject.create();
Observable<T> valueSequence = Observable.swithOnNextDelayError(callSequence.toSerialized());
your subscriber will be listening to the valueSequence, and whenever you need to "restart", you will call this -
callSequence.onNext(call.cache()); // *call* is Observable<T>
(i leave the .subscribeOn/.observeOn configuration to you)
I have this simple retrofit2 api interface which contains
interface Api {
#GET(BuildConfig.END_POINT) Observable<Response> fetchData();
}
So everything is fine when I'm doing a fresh request
but let say I fire a request and I un-subscribe immediately and then I try to fire new request it returns nothing.
So, in code it looks something like this:
in Activity::onPause I perform un-subscription and in Activity::onResume I fire the request again.
My request looks something like this::
api.fetchData()
.timeout(30,TimeUnit.SECONDS)
.doOnNext(new Action1<Response>() {
#Override public void call(Response response) {
list = response.getDataForList();
}
}).flatMap(new Func1<Response, Observable<List<Object>>>() {
#Override public Observable<Object>> call(Response response) {
return Observable.just(list);
}
});
When I tried debugging it, the call is made but doOnNext() is not called. None of the lifecycle methods are called.
And just for clarification from here I'm just returning the observable which I'm using it somewhere else where I'm observing on main thread and subscribing on IO.
Don't use doOnNext, just map. And try to get use to use lambdas, make your code much readable.
api.fetchData()
.timeout(30,TimeUnit.SECONDS)
.map(response -> response.getDataForList())
.flatMap(list -> Observable.just(list));
Now as concept, every time that some observer subscribe to this observable consume the items, then observable automatically unsubscribe the observer. So you don't have to worry about unsubscribe anything.
You can see some practical examples here. https://github.com/politrons/reactive
I have an Observable and subscribe to it. I need to not miss any emitted result, so I use onBackpressureBuffer like following:
Observable<Data> observable = observable.onBackpressureBuffer();
if (BuildConfig.DEBUG)
{
observable
.subscribeOn(HandlerScheduler.from(dataManager.getBackgroundHandler()))
.observeOn(HandlerScheduler.from(dataManager.getBackgroundHandler()))
.subscribe(new MeasuringSubscriber(...));
}
// Here is the real observer that I need in my app
observable
.subscribeOn(HandlerScheduler.from(dataManager.getBackgroundHandler()))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Data>()
{
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Data data) {
}
});
The MeasuringSubscriber is a custom subscriber that just logs how long a task needs, that's all.
Problem
If I add the MeasuringSubscriber, the subscribers do not work anymore and never emit a result. Why? And how can I make that working?
EDIT - NEW PROBLEM
Currently it's working, but the MeasuringSubscriber is somehow blocking, meaning, first all items are emitted one by one to the MeasuringSubscriber and only afterwards all items are emitted one by one to the main subscriber... Any ideas what could cause that?
I have a solution for that - I can extend my main observalbe from the MeasuringObservable - but I rather would like to know why this happens and how to avoid this...
I tried using publish + connect, but still it does emit all items to the first subscriber before emitting them to the second one...
How does using RxJava (or RxAndroid,etc) instead of AsyncTask in Android help prevent a context leak? In AsyncTask, if you execute it and user leaves the app, then the activity context can be null and the app can crash. I have heard that RxJava can help prevent this type of crash when doing threading. I also heard that it can do better error handling then the doInBackground method of AsyncTask (which handles errors badly). Most of the time I just return null (for example) in doInBackground if anything fails, but I've read that RxJava can return the exact error and not leak. Can anyone give an example?
Here is a small demo of a crash in AsyncTask if the user leaves the app while it's trying to report results to UI:
#SuppressWarnings("unused")
private class GetTask extends AsyncTask<Void, Void, Void> {
#Override
protected void onPostExecute(String result) {
pd = new ProgressDialog(getActivity().getApplicationContext());//can crash right here
pd.setTitle("Grabbing Track!");
pd.setMessage("Please wait...");
pd.setCancelable(false);
pd.setIndeterminate(true);
pd.show();
}}
And here is a doInBackground method call that does not send out errors that are useful:
#Override
protected String doInBackground(String... params) {
String myIntAsString = 1/0 + ""; //this should give an error (how do we report it to the caller??
//or if we are parsing json and it fails, how do we report it to the caller cleanly. Can RxJava help?
}
I think the good thing about RxJava is if you have a bunch of tasks you can put them in a sequence such that you know when one finishes and the next is about to start. in a AsyncTask if you have more then one running, you have NO guarantee which task will complete first and then you have to do alot of error checking if you care about order. So RxJava allows you to sequence calls.
In regards to memory leaks we can have a AsyncTask as an inner class of an activity. Now since its tied to the activity when the activity is destroyed that context is still hanging around and wont be garbage collected, thats the memory leak part.
Here is where RxJava can help. if any errors occur at all then we can call the subscribers onError method. The subcriber can look like this:
public Observable<JsonObject> get_A_NetworkCall() {
// Do your network call...but return an observable when done
}
Subscription subscription = get_A_NetworkCall()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<jsonResponse>() {
#Override
public void onCompleted() {
// Update UI
}
#Override
public void onError() {
// show error on UI
}
#Override
public void onNext(JsonObject response) {
// Handle result of jsonResponse
}
});
or something similar - this is psuedocode . The point is you can report the errors more cleanly and switch threads in one line. Here we are reporting on androids main thread but doing the work on a new thread. After we are done in our activities onDestroy method we can simply unsubscribe to the observable and it kills it and prevents any memory leaks that we encounter with AsyncTask. This to me should be the replacement for any asyncTasks.
I use a combination of two things. First, RxAndroid is really helpful:
https://github.com/ReactiveX/RxAndroid
You can use AppObservable.bindActivity on it to bind an observable such that its output is observed on the main thread, and if the activity is scheduled to be destroyed, messages will not be forwarded. You still have to manage the pause/resume lifecycle though. For that, I use a composite subscription like this (pseudojava coming up):
public class MyActivity extends Activity {
private final CompositeSubscription subscriptions = new CompositeSubscription();
#Override
public void onResume() {
super.onResume();
subscriptions.add(AppObservable.bindActivity(this, myObservable)
.subscribe());
subscriptions.add(AppObservable.bindActivity(this, myOtherObservable)
.subscribe());
}
#Override
public void onPause() {
subscriptions.clear();
super.onPause();
}
}
Obviously you'd want to do more in subscribe if you want to do something with the data, but the important thing is to collect the returned Subscription instances and add them to the CompositeSubscription. When it clears them out, it will unsubscribe them as well.
Using these 'two weird tricks' should keep things from coming back to the Activity when it is an inoperative state.
In Why is my timer Observable never called?
#Miguel Lavigne says:
"Keep in mind that if you're using Observables from within a Fragment or an Activity you should always make sure you unsubscribe from your Observables to eliminate chances of memory leaks."
It is clear to me how it works as long as I am using an Observable in an Activity, Fragment or View. But what if I am using it where there is no context?
My situation: I am having an external library which holds an object model. Each object has a .save() function, which is called from the UI. In save, an API endpoint call is performed by an Observable asynchronously.
Example:
public Overlay save() {
Observable.create(new Observable.OnSubscribe<Overlay>() {
#Override public void call(Subscriber<? super Overlay> subscriber) {
try {
Overlay overlay= OverlayEndpoint.insertOverlay(this); // call the API endpoint here
subscriber.onNext(overlay);
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Overlay>() {
#Override public void call(Overlay overlay) {
// process the saved result [omitted for brevity]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
// put the overlay into a local upload queue in case the endpoint is unreachable [omitted]
}
});
return this; // return the call immediately
}
The Observable is for one-time use within save and becomes obsolete thereafter. How can I make sure it does not persist?
Situation 1: Normal unsubscribe. Is there a way to unsubscribe right from within the call(), once processing is complete?
Situation 2: For whatever reason the Observable stays in memory. Could I use .timeout() to ensure the Observable is destroyed after enough time has passed?
Situation 1: Normal unsubscribe. Is there a way to unsubscribe right from within the call(), once processing is complete?
In your case, Observable will be in memory before Action1<Overlay> or new Action1<Throwable> is called. But after one of them is called, GC should be able to clean the Observable.
Situation 2: For whatever reason the Observable stays in memory. Could I use .timeout() to ensure the Observable is destroyed after enough time has passed?
In your case, Schedulers.newThread() will create a new Thread to run the Observable.OnSubscribe<Overlay>.call. So if this method has not returned yet, such as OverlayEndpoint.insertOverlay will run about 10 minutes, Observable can not be cleaned by GC because this thread is still using it. There is nothing we can do unless there is an approach to cancel OverlayEndpoint.insertOverlay.