Best practice to implement Retrofit callback to recreated activity?
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.htmlIt 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));