Best practice for implementing Retrofit callback for recreated activities?

I am switching to Retrofit and trying to figure out the correct architecture for use with asynchronous 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 the main action:

 RestAdapter restAdapter = new RestAdapter.Builder() .setServer("WEBSITE_URL") .build(); RESTService api = restAdapter.create(RESTService.class); api.getUserName(userId, new Callback<Response> {...}); 

Then the user rotates the device, and I have a new activity ... What happened here? How can I get a response to a new activity (I assume that the api call in the background will take longer than the first life cycle). Maybe I should use a static callback instance or what? Please show me the right way ...

+64
android rest web-services retrofit
Feb 16 '14 at 1:47
source share
9 answers

Use otto . There are many samples for mixing otto and retrofitting, 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 almost all the questions.

+42
Apr 15 '14 at 20:10
source share

For potential long server calls, I use AsyncTaskLoader . For me, the main advantage of Loaders is the processing of the activity life cycle. onLoadFinished is called only if your activity is visible to the user. Loaders are also split between a change in activity / fragment and orientation.

So, I created ApiLoader, which uses modified 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 run this bootloader as follows:

 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 several times, use restartLoader instead of initLoader.

+32
Mar 14 '14 at 9:21
source share

In my Android apps, I used some kind of MVP (ModelViewPresenter). For the “Modification” request, I did the “Assignment” operation to the corresponding Presenter, which, in turn, makes a request for reinstallation, and as a parameter I send a callback with a user listener attached to it (implemented by the master). When the callbacks reach the onSuccess or onFailure , I call the corresponding Listener methods that call Presenter, and then the Activity: P methods

Now, if the screen is rotated when my activity is recreated, it joins the Leader. This is done using a custom implementation of the Android application, where it stores an instance of speakers and uses a map to restore the correct presenter according to the Activity class.

I don't know if this is the best way, maybe @pareshgoel's answer is better, but it worked for me: D

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 in the overrode methods, call the presenter method, which will make an Activity call. And call where you want the leader to start everything: P

 Request rsqt = restAdapter.create(Request.class); rsqt.get(new RequestCallback<YourExpectedObject>(listener)); 

Hope this helps you.

+19
Mar 14 '14 at 6:45
source share

Firstly, your activity flows here because this line: api.getUserName (userId, new Callback {...}) creates an anonymous callback class that contains links to MainActivity. When the device rotates before calling Callback, then MainActivity will not be garbage collected. Depending on what you are doing in Callback.call (), your application may perform undefined behavior.

The general idea for handling such scenarios is:

  • Never create a non-static inner class (or an anonymous class, as indicated in the problem).
  • Instead, create a static class containing the WeakReference <> value for Activity / Fragment.

The above simply prevents leaks. It still will not help you return the Restore request to its activity.

Now, in order to return the results to your component (activity in your case) even after changing the configuration, you might want to use a mute saved fragment attached to your activity, which makes a call for re-equipment. More about Retained snippet - http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

The general idea is that Fragment automatically joins the Activity when the configuration changes.

+10
Mar 08 '14 at 9:44
source share

I highly recommend you watch this video listed on Google I / O.

It talks about how to create REST requests by delegating them to a service (which almost never gets killed). When the request is completed, it will immediately be saved in the built-in Android database, so the data will be immediately available when your activity is ready.

With this approach, you never have to worry about the activity life cycle, and your requests are handled in a much more untied way.

The video does not specifically talk about retrofitting, but you can easily adapt the modification for this paradigm.

+4
Aug 04 '15 at 15:47
source share

Using Retrofit2 to handle orientation changes. I was asked about this at an interview and he was rejected for not knowing about it at the time, but here he 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(); } } } } 
+3
Jan 21 '17 at 0:18
source share

Use robospice

All components of your application that require data are registered with the spice service. The service will take care of sending your request to the server (using the upgrade, if you want). When the response is returned, all registered components are notified. If one of them is no longer available (for example, an activity that was triggered due to rotation), it simply was not notified.

Advantages: one request that was not lost, regardless of whether you rotate your device, open new dialogs / fragments, etc.

+2
Jul 16 '15 at 20:42 on
source share

When I was new to retrofitting, I found out the best way from these sites

Consumption api with modification

update tutorial

+2
Jul 28 '15 at 9:07
source share

This is a typical limitation of these kinds of decisions; by default, Android creates a new action when the user rotates the device.

However, in this case there is an easy way, configure the orientation listener. By doing this, Android will not create new activity.

  <activity ... android:configChanges="orientation" /> 

Another way to fix this is through modifications with other frameworks such as Robospice.

-36
Mar 06 '14 at 16:46
source share



All Articles