Why is disposing DisposableObserver is important in this case - android

I working on android project with clean architecture.
I have the below class:
public abstract class RxBaseInteractor<T, Params> {
private final CompositeDisposable disposables;
public RxBaseInteractor() {
this.disposables = new CompositeDisposable();
}
abstract public Observable<T> buildUseCaseObservable(Params params);
public void execute(DisposableObserver<T> observer, Params params) {
Preconditions.checkNotNull(observer);
final Observable<T> observable = this.buildUseCaseObservable(params)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
addDisposable(observable.subscribeWith(observer));
}
public void dispose() {
if (!disposables.isDisposed()) {
disposables.dispose();
}
}
protected void addDisposable(Disposable disposable) {
Preconditions.checkNotNull(disposable);
Preconditions.checkNotNull(disposables);
disposables.add(disposable);
}
}
So execute(..) take a DisposableObserver and then there is a dispose() method which is called to dispose this observable.
In my case the observable may come from WebApi using retrofit or cache using Realm.
Now in the presenter onDestroy(), i called the interactor.dispose() like:
#Override public void destroy() {
super.destroy();
myInteractor.dispose();
}
which is called after that from the view:
#Override public void onDestroy() {
super.onDestroy();
if (getPresenter() != null) {
getPresenter().destroy();
}
}
I fully understanding the architecture and also i understand disposing un-managed network or database resources but i need to fully understand if in this case the dispose of observable really matter as i thought that Retrofit or Realm auto manage closing a connections and disposing there resources.
I think it's not related to dispose realm or retrofit resources but it may be related to unsubscribe on the observable it self as i checked the documentation and i found :
Class DisposableObserver: An abstract Observer that allows asynchronous cancellation by
implementing Disposable. All pre-implemented final methods are
thread-safe.
Use the public dispose() method to dispose the sequence from within an
onNext implementation.
But i still not understand the benefits of using it. Is it for unsubscribe from the observable when destroying the view so it will go from onNext() to onComplete() and close the subscription on the emitter?

The reason behind using dispose method is because after the system initiate the view (activity or fragment), the subscription gets start and then you have decided to go back or initiate another view while the older subscription is still getting executed and didn't finish its job. This means that it's still in the memory which will cause a memory leak. So you have to call dispose method for unsubscribe.

Adding more to #abozaid's answer, When older subscription is still On and in the meantime, our user switches to other view (activity or fragment) or closes older view (or application itself), it'll definitely leak memory.
But, if we were observing observable for UI updation with AndroidSchedulers.mainThread() scheduler, then our code would crash because at the time of updating UI, the view and context would have gone away (or destroyed).
myObservable.observeOn(AndroidSchedulers.mainThread()) // like this
One other point, I can add here is that, even if we handle the crash by putting precaution in code, the subscription running unused would hamper performance at some stage.

Related

Avoiding memory leak while calling retrofit 2

By following this article I found that calling Retrofit enqueue() on onCreate() method may cause a memory leak.
Here is what the article says, doing this:
Calling Retrofit in the main thread
public class MoviesActivity extends Activity {
private TextView mNoOfMoviesThisWeek;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_movies_activity);
mNoOfMoviesThisWeek = (TextView) findViewById(R.id.no_of_movies_text_view);
MoviesRepository repository = ((MoviesApp) getApplication()).getRepository();
repository.getMoviesThisWeek()
.enqueue(new Callback<List<Movie>>() {
#Override
public void onResponse(Call<List<Movie>> call,
Response<List<Movie>> response) {
int numberOfMovies = response.body().size();
mNoOfMoviesThisWeek.setText("No of movies this week: " + String.valueOf(numberOfMovies));
}
#Override
public void onFailure(Call<List<Movie>> call, Throwable t) {
// Oops.
}
});
}
}
Now if this network call runs on a very slow connection and before the call ends, the Activity is rotated or destroyed somehow, then the entire Activity instance will be leaked.
I tried to do the same thing on my app. I called a big content (240 objects) usign enqueue() in onCreate() method. Then while the content was loading I rotated the device multiple times and LeakCanary showed me a memory leak in the Activity as the article said.
Then I tried two approachs to avoid the memory leak:
First option
Calling retrofit execute() method on a background thread using static inner class.
Calling Retrofit in a background thread
private static class RetrofitCall extends AsyncTask<Void, Void, List<Show>> {
private WeakReference<TextView> numberOfShows;
public RetrofitCall(TextView numberOfShows) {
this.numberOfShows = new WeakReference<>(numberOfShows);
}
#Override
protected List<Show> doInBackground(Void... voids) {
List<Show> showList = new ArrayList<>();
if (!isCancelled()) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(TvMazeService.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
TvMazeService service = retrofit.create(TvMazeService.class);
try {
Response<List<Show>> response = service.getShows().execute();
if (response.isSuccessful()) {
showList = response.body();
}
return showList;
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
#Override
protected void onPostExecute(List<Show> shows) {
super.onPostExecute(shows);
TextView textView = numberOfShows.get();
if (textView != null) {
String number = String.valueOf(shows.size());
textView.setText(number);
}
}
}
Then I tried to get the memory leak using LeakCanary again and it happened that the memory leak was gone.
Second option
Using ViewModel.
As you can see in the documentation, while using ViewModel I called retrofit asynchronous in the ViewModel class and when the screen is rotated (activity is destroyed) it does not need to load the data again as it remains saved.
This approach also did not give the a memory leak and was the best while talking about memory.
Questions
1) Then, using ViewModel to call Retrofit is the best option and it really avoid memory leak?
2) Is there any problem to call retrofit using enqueue() in onCreate() as MoviesActivity does?
3) In this approaches, which one is the best to make a call to authenticate a user?
1) Using ViewModel in the correct way does not cause memory leaks and is a good option. You can see the google's video explanation, and also this lecture talking about the difference between MVP and MVVM. This second lecture gives a really good explanation about the topic.
2) Calling retrofit enqueue() in onCreate() is a problem and it causes a memory leak. The problem is that the first time you start your activity it calls retrofit, then when you rotate your device, all the activity is destroyed and recreated again. If you rotate the device before the data is loaded completed, retrofit will be called for the second time when onCreate() is called again, and if you keep doing it 10 times, retrofit will be called 10 times, and then you stop rotating the device. The result from the calls will start to come, bzzz :( the result will be displayed 10 times because you called it 10 times. This implies in a huge memory leak. If you implement this approach and use LeakCanary you will see the leak.
3) What is the best approach?
Using enqueue() method in onCreate() is definitely not good.
Static inner classes (using AsyncTask) is good, but it does not survive to configuration changes because you need to cancel it in onDestroy(). This is why it does not cause a memory leak because the Task is canceled in onDestroy().
MVP is a really good approach for making retrofit calls. You can learn more in this medium article and the source code is here.
Read about the differences between MVP and MVVM as in this article.
Finally, Google is advising devs to use ViewModel in these scenarios.
You can follow my discussion in another question. Where we are talking about the same subject but while sign in a user to the server.
The reason why you will got memory leaks if calling enqueue() in onCreate() is that the enqueued calls will hold a reference to your activity instance, because the callback instance(anonymous class) passed to it is holding a reference to the enclosing class instance. As long as you cancel it before onDestroy(), there won't be a problem.

RxJava Android orientation change and network request

I'm new to RxJava and I need to integrate it into an existing project. I need to refactor the existing code, adding Observables for networking (Socket IO).
Currently when a network request is made (client -> server) a callback (interface) is added to a HashMap and once the request is completed, it will deliver the data back to the caller:
// Singleton
public class API {
public void checkTicket(String ticketId, final String networkRequestId, Callback callback) {
// Add the callback to the hashmap
registerCallback(networkRequestId, callback);
JSONObject json = RequestFactory.createTicketCheckerRequest(ticketId);
// Make the network request
getSocket().checkTicket(json, new Callback() {
#Override
public void onRequestDone(Response response) {
// Retrieve the callback
callback = getCallback(networkRequestId);
// Don't keep reference, remove from hashmap
unsubscribeCallback(networkRequestId);
// Check if it's unsuccessful and build the corresponding error response
if (!response.isSuccess()) {
// build custom error response
response = ResponseFactory.buildError(response);
}
// Deliver response from server
callback.onRequestDone(response);
}
});
}
}
It can be called from Activities and Fragments:
private void checkTicket() {
String ticketId = editText.getText().toString();
API.getInstance().checkTicket(ticketId, REQUEST_ID_CHECK_TICKET, new Callback() {
#Override
protected void onRequestDone(Response response) {
textView.setText(response.getData());
}
});
}
#Override
public void onDestroy() {
super.onDestroy();
// Removes callback from HashMap in case of the UI is destroyed before the arrives
API.getInstance().unsubscribe(REQUEST_ID_CHECK_TICKET);
}
The above code works but it's really tight with the UI's lifecycle and sometimes it's causing memory leak, because onDestroy() is not getting called (if you navigate between activities and Android OS kills the "paused" activities from the stack) or because the anonymous inner classes (callbacks) which are holding a reference to the UI, and from now on I need to support orientation change.
This is the code that I have implemented using RxJava:
API:
public Observable<Response> checkTicket(String ticketId) {
return Observable.create(subscriber -> {
JSONObject json = RequestFactory.createTicketCheckerRequest(ticketId);
// Make the network request
getSocket().checkTicket(json, new Callback() {
#Override
public void onRequestDone(Response response) {
subscriber.onNext(response);
subscriber.onComplete();
}
});
});
}
This is how it's called from the UI:
private CompositeDisposable mDisposables = new CompositeDisposable();
private void checkTicket() {
//////
Disposable disposable = API.getInstance().checkTicket(ticketId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(result -> {
textView.setText(result.getData());
});
mDisposables.add(disposable);
}
#Override
public void onStop() {
super.onStop();
if (!mDisposables.isDisposed()) {
mDisposables.dispose();
}
}
The above RxJava is working, however if an orientation change occurs the data is not returned because the Observer is unsubscribed.
Is the above implementation correct?
How should I subscribe without executing the request? Subscribe and wait for data change.
Another alternative would be EventBus but this is just Plan B. EventBus fits exactly my requirements, subscribe and wait for data change, but I want to evict boilerplate.
I have read other articles by using Fragment's setRetainInstance(true) but what if I need to use it from an Activity? What if I don't want to retain the state of the Fragment?
People suggested to use MVVM or MVP architecture, but I don't have the time to refactor the entire project.
I will suggest you move to MVVM. With your presented code, it is not that hard. Here is a sample code of how it will look like
Your ModelView
public class MyViewModel extends ViewModel {
private CompositeDisposable mDisposables = new CompositeDisposable();
private MutableLiveData<Response> response;
public LiveData<Response> getResponse() {
if (response == null) {
response = new MutableLiveData<Response>();
loadData();
}
return response;
}
private void loadData() {
Disposable disposable = API.getInstance().checkTicket(ticketId)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(result -> {
response.postValue(result.getData());
});
mDisposables.add(disposable);
}
void onCleared()
{
super.onCleared();
mDisposables.clear(); //no more leaks. It takes care of lifecycle for you
}
}
Your Activity
public class MyActivity extends AppCompatActivity {
public void onCreate(Bundle savedInstanceState) {
MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
model.getResponse().observe(this, response -> {
// update UI
textView.setText(response); //response = Response object from Live data
});
}
}
If you don't want to handle configuration changes and want to cache data from observables, you can use BehaviorSubjects and a hot observable. This will allow you to get the most recent item that the observable published.
Other than that, I suggest you use the ViewModel from the architecture components. It will allow you to create a component that is bound to the activity but will not be affected by the lifecycle (except termination, obviously). Surprisingly enough, ViewModelProviders are implemented as fragments with setRetainInstance(true). You don't have to completely refactor the entire app. Just move the ones that you want to preserve during configuration changes.
You need to consider the logical scope of your network requests, and this is entirely separate from whether you're using RxJava. Background tasks like network requests need to be owned by an Android component (Application, Activity, etc.) with the appropriate lifetime. The usual way to make activity-scoped background tasks survive a config change is to host them in a retained fragment. You would still do that if you were using RxJava.
Android OS kills the "paused" activities from the stack
This doesn't happen unless something has changed in Android 8 or newer. The documentation suggests that the framework could destroy individual activities in the backstack, but currently it only destroys the entire task when it's in the background. Your app is correct and future-proof if and only if it works with the "don't keep activities" developer option on.

Why Rxjava could cause memory leaking

I'm playing with rxjava and found there is a risk of memory leak if a subscription is not completed before an activity is destroyed because "observables retain a reference to the context". One of the demos for such case is given as below, if the subscription is not unsubscribed onDestroyed (source: https://github.com/dlew/android-subscription-leaks/blob/master/app/src/main/java/net/danlew/rxsubscriptions/LeakingActivity.java):
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_leaking);
// This is a hot Observable that never ends;
// thus LeakingActivity can never be reclaimed
mSubscription = Observable.interval(1, TimeUnit.SECONDS)
.subscribe(new Action1<Long>() {
#Override public void call(Long aLong) {
Timber.d("LeakingActivity received: " + aLong);
}
});
}
However I'm not sure why such a leak exists. I've checked the Observable class and seen nothing relevant to Context. So all I can think of is because there is an anonymous Action1 class defined within the subscribe method which hold a reference to the activity instance. And the observable in turn holds a reference to the action. Am I right?
Thanks
The .subscribe(new Action1<Long>() { }) creates and stores nested class which as any non-static nested class has reference to containg class instance - in this case the Activity.
To resolve that you can Subscription.unsubscribe the mSubscription in the Activity.onDestroy

When should I unsubscribe from observables in activities and fragments to avoid negative consequences?

I have subscribers that interact with UI components of an activity/fragment. I'm not sure where and when should I unsubscribe them. I see two ways: the former is to unsubscribe them in the onPause() method, the latter is to unsubscribe them in the onStop() method. Which one is correct and why? Thanks
You should use onPause as you don't need the listener running while the activity or fragment is not. onPause gets called everytime the item is no longer activity running. onStop is called on the way to destruction.
"But what if my observable downloads some content after the activity starts"
For long time operations or operations that must be independent from Activity Lifecycle you shuold use Service component
Please take a look at this https://github.com/trello/RxLifecycle. It will prevent your app from throwing NPE on views and context memory leaks.
Read about MVP pattern. Below I made exemplary implementation
Presenter class
public class SamplePresenter {
#NonNull
private final Observable<SomeData> someDataObservable;
public SamplePresenter(#NonNull ApiService apiService) {
someDataObservable = apiService.apiRequest()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io());
}
#NonNull
public Observable<SomeData> getSomeDataObservable() {
return someDataObservable;
}
}
MainActivity class
#Inject
SamplePresenter samplePresenter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
samplePresenter.getSomeDataObservable()
.compose(this.<SomeData>bindToLifecycle())
.subscribe(new Action1<SomeData>() {
#Override
public void call(SomeData someData) {
}
});

Best practice to implement Retrofit callback to recreated activity?

I'm switching to Retrofit and trying to understand proper architecture for using it with async callbacks.
For example I have an interface:
interface RESTService{
#GET("/api/getusername")
void getUserName(#Query("user_id") String userId,
Callback<Response> callback);
}
And I run this from main activity:
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("WEBSITE_URL")
.build();
RESTService api = restAdapter.create(RESTService.class);
api.getUserName(userId, new Callback<Response> {...});
Then user rotates the device and I have newly created activity... What was happen here? How can I get response to the new activity (I assume that api call in background will execute longer than first activity life). Maybe I must use static instance of callback or what? Please show me the right way...
Use otto.
There are a lot of samples to mix otto and retrofit, for example https://github.com/pat-dalberg/ImageNom/blob/master/src/com/dalberg/android/imagenom/async/FlickrClient.java
Or read this post http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
It answers on almost all questions
For potential long running server calls i use an AsyncTaskLoader. For me, the main advantage of Loaders are the activity-lifecycle handling. onLoadFinished is only called if your activity is visible to the user. Loaders are also shared between activity/fragment and orientation changes.
So i created an ApiLoader which uses retrofits synchronous calls in loadInBackground.
abstract public class ApiLoader<Type> extends AsyncTaskLoader<ApiResponse<Type>> {
protected ApiService service;
protected ApiResponse<Type> response;
public ApiLoader(Context context) {
super(context);
Vibes app = (Vibes) context.getApplicationContext();
service = app.getApiService();
}
#Override
public ApiResponse<Type> loadInBackground() {
ApiResponse<Type> localResponse = new ApiResponse<Type>();
try {
localResponse.setResult(callServerInBackground(service));
} catch(Exception e) {
localResponse.setError(e);
}
response = localResponse;
return response;
}
#Override
protected void onStartLoading() {
super.onStartLoading();
if(response != null) {
deliverResult(response);
}
if(takeContentChanged() || response == null) {
forceLoad();
}
}
#Override
protected void onReset() {
super.onReset();
response = null;
}
abstract protected Type callServerInBackground(SecondLevelApiService api) throws Exception;
}
In your activity you init this loader like this:
getSupportLoaderManager().initLoader(1, null, new LoaderManager.LoaderCallbacks<ApiResponse<DAO>>() {
#Override
public Loader<ApiResponse<DAO>> onCreateLoader(int id, Bundle args) {
spbProgress.setVisibility(View.VISIBLE);
return new ApiLoader<DAO>(getApplicationContext()) {
#Override
protected DAO callServerInBackground(ApiService api) throws Exception {
return api.requestDAO();
}
};
}
#Override
public void onLoadFinished(Loader<ApiResponse<DAO>> loader, ApiResponse<DAO> data) {
if (!data.hasError()) {
DAO dao = data.getResult();
//handle data
} else {
Exception error = data.getError();
//handle error
}
}
#Override
public void onLoaderReset(Loader<ApiResponse<DAO>> loader) {}
});
If you want to request data multiple times use restartLoader instead of initLoader.
I've been using a kind of MVP (ModelViewPresenter) implementation on my Android apps. For the Retrofit request I made the Activity calls it's respective Presenter, which in turn makes the Retrofit Request and as a parameter I send a Callback with a custom Listener attached to it (implemented by the presenter). When the Callback reach onSuccess or onFailure methods I call the Listener's respective methods, which calls the Presenter and then the Activity methods :P
Now in case the screen is turned, when my Activity is re-created it attaches itself to the Presenter. This is made using a custom implementation of Android's Application, where it keeps the presenters' instance, and using a map for recovering the correct presenter according to the Activity's class.
I don't know if it's the best way, perhaps #pareshgoel answer is better, but it has been working for me.
Examples:
public abstract interface RequestListener<T> {
void onSuccess(T response);
void onFailure(RetrofitError error);
}
...
public class RequestCallback<T> implements Callback<T> {
protected RequestListener<T> listener;
public RequestCallback(RequestListener<T> listener){
this.listener = listener;
}
#Override
public void failure(RetrofitError arg0){
this.listener.onFailure(arg0);
}
#Override
public void success(T arg0, Response arg1){
this.listener.onSuccess(arg0);
}
}
Implement the listener somewhere on the presenter, and on the overrode methods call a presenter's method that will make the call to the Activity. And call wherever you want on the presenter to init everything :P
Request rsqt = restAdapter.create(Request.class);
rsqt.get(new RequestCallback<YourExpectedObject>(listener));
Firstly, your activity leaks here because this line:
api.getUserName(userId, new Callback {...})
creates an anonymous Callback class that holds a strong reference to you MainActivity. When the device is rotated before the Callback is called, then the MainActivity will not be garbage collected. Depending on what you do in the Callback.call(), your app may yield undefined behaviour.
The general idea to handle such scenarios is:
Never create a non-static inner class (or an anonymous class as mentioned in the problem).
Instead create a static class that holds a WeakReference<> to the Activity/Fragment.
The above just prevents Leaks. It still does not help you get the Retrofit call back to your Activity.
Now, to get the results back to your component (Activity in your case) even after configuration change, you may want to use a headless retained fragment attached to your Activity, which makes the call to Retrofit. Read more here about Retained fragment - http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
The general idea is that the Fragment automatically attaches itself to the Activity on configuration change.
I highly recommend you watch this video given at Google I/O.
It talks about how to create REST requests by delegating them to a service (which is almost never killed). When the request is completed it is immediately stored into Android's built-in database so the data is immediately available when your Activity is ready.
With this approach, you never have to worry about the lifecycle of the activity and your requests are handled in a much more decoupled way.
The video doesn't specifically talk about retrofit, but you can easily adapt retrofit for this paradigm.
Use Robospice
All components in your app which require data, register with the spice service. The service takes care of sending your request to the server (via retrofit if you want). When the response comes back, all components which registered get notified. If there is one of them not available any more (like an activity which got kicked because of rotation), it's just not notified.
Benefit: One single request which does not get lost, no matter whether you rotate your device, open new dialogs/fragments etc...
Using Retrofit2 to handle orientation change. I was asked this in a job interview and was rejected for not knowing it at the time but here it is now.
public class TestActivity extends AppCompatActivity {
Call<Object> mCall;
#Override
public void onDestroy() {
super.onDestroy();
if (mCall != null) {
if (mCall.isExecuted()) {
//An attempt will be made to cancel in-flight calls, and
// if the call has not yet been executed it never will be.
mCall.cancel();
}
}
}
}

Categories

Resources