RxJava Android orientation change and network request - android

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.

Related

Why is disposing DisposableObserver is important in this case

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.

Android manage multi request rxJava on rotation device

I'm using MVVM on android application and i want to manage requests and rxJava on device rotation, how can i disable request after rotation device and countinue from last request?
this is my simple code to know how can i doing that, but i can't find any document and sample code about it
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_register);
...
Observer<String> myObserver = new Observer<String>() {
#Override
public void onError(Throwable e) {
// Called when the observable encounters an error
}
#Override
public void onComplete() {
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
// Called each time the observable emits data
Log.e("MY OBSERVER", s);
}
};
Observable.just("Hello").subscribe(myObserver);
}
I'm using latest version of rxJava
Handling rotation is a cool challenge in Android. There're a few ways to do that.
1- Services: You can use a service and handle your network requests or other background operations in service. Also with Services, you'll seperate your business logic from ui.
2- Worker Fragment: Worker fragment is a fragment instance without a layout. You should set your worker fragment's retainInstanceState to true. So you'll save your fragment from orientation change and will not lose your background operations.
Why Worker Fragment? If you set retainInstanceState true to a fragment with layout, you'll leak views.
If you're using MVVM you can implement ViewModel as a Worker Fragment which as setRetainInstanceState = true
3- Global Singleton Data Source: You can create a global singleton data source class which handles your operations in an independent scope from Activity / Fragment lifecycle in your application.
4- Loaders: Loaders can recover state from orientation changes. You handle your operations with loaders but they are designed to load data from disk and are not well suited for long-running network requests.
Extra: You can use Path's Priority Job Queue to persist your jobs:
https://github.com/path/android-priority-jobqueue
Edit: You can check my repo for handling device rotation without using Google's new architecture components. (As an example of Worker Fragment which i pointed in my answer.)
https://github.com/savepopulation/bulk-action
You have the following options:
Use some global Singleton, or your Application class, that holds your logic, not within your Activity's lifecycle
Use a Service that runs next to your activity/application
Use a Loader
Global state is often bad and makes your code hard to test / debug. Services tend to be overkill.
For your use case of device rotation and continuing where one left off you'd usually use a Loader, which keeps running on rotation and only gets destroyed once you leave the activity.
I also recently wrote an article about one possible solution to use Loaders together with RxJava to keep state during orientation changes.
You can take advantage of Fragment#setRetainInstance(true). With that flag set, fragment is not destroyed after device rotation and can be used as an object container. Please look at this sample which also stores Observable - https://github.com/krpiotrek/RetainFragmentSample
you need to override
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}
When device is rotated store data in bundle then inside on create check
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(savedInstanceState == null){
//saved instance is null
}else{
//get your stored values here
counter = savedInstanceState.getInt("value",0); //here zero is the default value
}
}
How I'm doing this is to have a singleton class (or any long living Object as explained by savepopulation earlier, but - the trick is to store the loaded data in a BehaviorSubject, and subscribe to that subject in the Activity instead of the original network request.
This way:
public class MyNetworkSingleton {
// This static service survives orientation changes
public static MyNetworkSingleton INSTANCE = new MyNetworkSingleton();
private final BehaviorSubject<String> dataSubject = BehaviorSubject.create();
public Observable<String> getData() {
if (!dataSubject.hasValue()) {
refreshData(); // No data is loaded yet, load initial data from network
}
return dataSubject;
}
public void refreshData() {
someDataSourceCall().subscribe(new Observer<String>() {
#Override
public void onError(Throwable e) {
// Remember, this point also needs error handling of some form,
// e.g. propagating the error to the UI as a Toast
}
#Override
public void onComplete() {
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String data) {
dataSubject.onNext(data); // this refreshes the internally stored data
}
});
}
private Observable<String> someDataSourceCall() {
return // some network request here etc. where you get your data from
}
}
and then:
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
Observer<String> myObserver = new Observer<String>() {
#Override
public void onError(Throwable e) {
// Called when the observable encounters an error
}
#Override
public void onComplete() {
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
// Called each time the observable emits data
Log.e("MY OBSERVER", s);
}
};
MyNetworkSingleton.INSTANCE.getData().subscribe(myObserver);
myRefreshButton.setOnClickListener(new Button.OnClickListener() {
public void onClick(View v) {
// refresh data from network only when button is pressed
MyNetworkSingleton.INSTANCE.refreshData();
}
});
}
This way only first time you need the data from network it will be loaded, or when the user clicks a refresh button (myRefreshButton).

How to update UI from Android service using RxJava/RxAndroid

I have a Bound Service which responsible for downloading files and thus it knows the downloading status/progress. And the UI (Fragment or Activity) has to show/update download progress from the service.
Actually i think the common way is to use BroadcastReciever or a CallBack from Activity. But i heard somewhere about using RxJava (ReactiveX Programming) and mediator class (and Dagger to inject it into both service and activity) which is mentioned below.
So my question is how to handle RxJava with these bunch of stuff? Any Code Samples? Is there another efficient way than using intents?
Resource:
More efficient way of updating UI from Service than intents? [ see the first answer update ]
Required of RxJava/RxAndroid
1) OBSERVABLES
2) OBSERVERS
3) SUBSCRIBE
Plan where you need to place your 1, 2, 3,
OBSERVABLES go where the data is created, so In your case SERVICE
OBSERVERS go where the data needs to be consumed(or displayed), so that's your ACTIVITY
SUBSCRIBE goes anywhere where you have access to OBSERVABLE & OBSERVER, so lets use ACVITITY for that
Procedure and Code:
Firstly,
Prepare your OBSERVABLE in service like this
class MyService extends Service {
static PublishSubject<String> data = PublishSubject.create();
#Override
public void onStarCommand(Intent intent,int i, int j){
# DO THIS ANYWHER WHERE YOU GENERATE DATA
data.onNext("Hello");
}
public static Observable<String> getObservable(){
return data;
}
}
Secondly,
Prepare your OBSERVER(and OBSERVABLE) in Activity's onCreate
Observable<String> observable = MyService.getObservable();
Observer<String> observer = new Observer<String>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted: ");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError: ",e);
}
#Override
public void onNext(String text) {
Log.d(TAG, "DATA reveived here: "+text);
}
};
Lastly
Link both OBSERVER and OBSERVABLE in Activity, else Observable will not respond,
use this again in onCreate
observable.subscribe(observer);
DONE, Now when even the data is triggered from Service using onNext(), the data arrives in Activity
The Binder subclass you use when something binds to your Service can expose a method that returns an Observable which emits progress data.
The caveat with this approach is that you have two resources which you need to release when the Activity instance is no longer valid: the service connection and the observable subscription.

RxJava as event bus?

I'm start learning RxJava and I like it so far. I have a fragment that communicate with an activity on button click (to replace the current fragment with a new fragment). Google recommends interface for fragments to communicate up to the activity but it's too verbose, I tried to use broadcast receiver which works generally but it had drawbacks.
Since I'm learning RxJava I wonder if it's a good option to communicate from fragments to activities (or fragment to fragment)?. If so, whats the best way to use RxJava for this type of communication?. Do I need to make event bus like this one and if that's the case should I make a single instance of the bus and use it globally (with subjects)?
Yes and it's pretty amazing after you learn how to do it. Consider the following singleton class:
public class UsernameModel {
private static UsernameModel instance;
private PublishSubject<String> subject = PublishSubject.create();
public static UsernameModel instanceOf() {
if (instance == null) {
instance = new UsernameModel();
}
return instance;
}
/**
* Pass a String down to event listeners.
*/
public void setString(String string) {
subject.onNext(string);
}
/**
* Subscribe to this Observable. On event, do something e.g. replace a fragment
*/
public Observable<String> getStringObservable() {
return subject;
}
}
In your Activity be ready to receive events (e.g. have it in the onCreate):
UsernameModel usernameModel = UsernameModel.instanceOf();
//be sure to unsubscribe somewhere when activity is "dying" e.g. onDestroy
subscription = usernameModel.getStringObservable()
.subscribe(s -> {
// Do on new string event e.g. replace fragment here
}, throwable -> {
// Normally no error will happen here based on this example.
});
In you Fragment pass down the event when it occurs:
UsernameModel.instanceOf().setString("Nick");
Your activity then will do something.
Tip 1: Change the String with any object type you like.
Tip 2: It works also great if you have Dependency injection.
Update:
I wrote a more lengthy article
Currently I think my preferred approach to this question is this to:
1.) Instead of one global bus that handles everything throughout the app (and consequently gets quite unwieldy) use "local" buses for clearly defined purposes and only plug them in where you need them.
For example you might have:
One bus for sending data between your Activitys and your ApiService.
One bus for communicating between several Fragments in an Activity.
One bus that sends the currently selected app theme color to all Activitys so that they can tint all icons accordingly.
2.) Use Dagger (or maybe AndroidAnnotations if you prefer that) to make the wiring-everything-together a bit less painful (and to also avoid lots of static instances). This also makes it easier to, e. g. have a single component that deals only with storing and reading the login status in the SharedPreferences - this component could then also be wired directly to your ApiService to provide the session token for all requests.
3.) Feel free to use Subjects internally but "cast" them to Observable before handing them out to the public by calling return subject.asObservable(). This prevents other classes from pushing values into the Subject where they shouldn't be allowed to.
Define events
public class Trigger {
public Trigger() {
}
public static class Increment {
}
public static class Decrement {
}
public static class Reset {
}
}
Event controller
public class RxTrigger {
private PublishSubject<Object> mRxTrigger = PublishSubject.create();
public RxTrigger() {
// required
}
public void send(Object o) {
mRxTrigger.onNext(o);
}
public Observable<Object> toObservable() {
return mRxTrigger;
}
// check for available events
public boolean hasObservers() {
return mRxTrigger.hasObservers();
}
}
Application.class
public class App extends Application {
private RxTrigger rxTrigger;
public App getApp() {
return (App) getApplicationContext();
}
#Override
public void onCreate() {
super.onCreate();
rxTrigger = new RxTrigger();
}
public RxTrigger reactiveTrigger() {
return rxTrigger;
}
}
Register event listener wherever required
MyApplication mApp = (App) getApplicationContext();
mApp
.reactiveTrigger() // singleton object of trigger
.toObservable()
.subscribeOn(Schedulers.io()) // push to io thread
.observeOn(AndroidSchedulers.mainThread()) // listen calls on main thread
.subscribe(object -> { //receive events here
if (object instanceof Trigger.Increment) {
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) + 1));
} else if (object instanceof Trigger.Decrement) {
if (Integer.parseInt(fabCounter.getText().toString()) != 0)
fabCounter.setText(String.valueOf(Integer.parseInt(fabCounter.getText().toString()) - 1));
} else if (object instanceof Trigger.Reset) {
fabCounter.setText("0");
}
});
Send/Fire event
MyApplication mApp = (App) getApplicationContext();
//increment
mApp
.reactiveTrigger()
.send(new Trigger.Increment());
//decrement
mApp
.reactiveTrigger()
.send(new Trigger.Decrement());
Full implementation for above library with example -> RxTrigger

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