Retrofit 2.0 - Custom CallAdapterFactory - Callbacks not happening on MainThread - android

I'm in the process of migrating my Android app to Retrofit 2.0. I had a custom ErrorHandler extending RetrofitError so I could react to different Http errors.
Now I understand I must create a custom CallAdapterFactory. I used the sample ErrorHandlingCallAdapter provided here.
My resulting CallAdapter is pretty much the same code, but if needed I could also post my code.
What's happening is that when I use this CallAdapterFactory, callbacks are not happening on the MainThread. I get android.view.ViewRootImpl$CalledFromWrongThreadException when trying to update the UI (which I always need to). I also don't want to always wrap my code with runOnUIThread in my callbacks.
I don't know if this helps, but when I log Thread.currentThread().getName() in my callbacks, it returns OkHttp.

I ended up passing an executor to my CallAdapter.Factory:
public static class MainThreadExecutor implements Executor {
private final Handler handler = new Handler(Looper.getMainLooper());
#Override
public void execute(#NonNull Runnable r) {
handler.post(r);
}
}
...
.addCallAdapterFactory(new ErrorHandlingCallAdapter.ErrorHandlingCallAdapterFactory(new MainThreadExecutor()))
and wrapping the callback in:
callbackExecutor.execute(new Runnable() {
#Override
public void run() {
}
});
I inspired myself from this.

You don't need to create a new executor, you can use retrofit's :
public AuthCall adapt(Call call) {
return new CustomCall(call, statuses, retrofit.callbackExecutor());
}
keep a reference of it in your adapted calls, and then use it within your callback:
customCall.enqueue(new Callback() {
executor.execute(...

Related

How to write unit test for RxJava CompositeSubscription

#Override public void onBarcodeReceived(final String barcode) {
view.showProgress();
if (!textUtil.isEmpty(barcode)) {
subscriptions.add(
interactor.getSearchResultByBarcode(barcode).subscribe(subscriberForSearchResults(true)));
}
}
private Subscriber<PriceAndStockActivityViewModel> subscriberForSearchResults(
boolean fromBarcode) {
return new BaseSubscriber<PriceAndStockActivityViewModel>() {
#Override public void onNext(PriceAndStockActivityViewModel priceAndStockActivityViewModel) {
super.onNext(priceAndStockActivityViewModel);
view.updateView(priceAndStockActivityViewModel);
}
#Override public void onError(Throwable e) {
super.onError(e);
view.hideProgress();
view.organizeScreenComponentsByVisibility(true);
view.onError(e);
}
};
}
I've wanted to test method called onBarcodeReceived like below
#Test public void should_updateViewByViewModel_when_AnyBarcodeReceived() {
String barcode = "123123123";
PriceAndStockActivityViewModel viewModel = getPriceAndStockActivityViewModel(barcode);
when(textUtil.isEmpty(barcode)).thenReturn(false);
when(interactor.getSearchResultByBarcode(anyString())).thenReturn(Observable.just(viewModel));
presenter.onBarcodeReceived(barcode);
verify(view).showProgress();
verify(interactor).getSearchResultByBarcode(anyString());
verify(view).updateView(any(PriceAndStockActivityViewModel.class));
}
Since onNext runs in a different thread its normal not to reach view.updateView. It looks simple but I couldn't find how to solve it. Is there any way to verify updateView?
I presume getSearchResultByBarcode() works on a background thread. So I wonder how you're able to change your UI from this background thread?
I'd change the execution of your subscriber to Android's main thread, so that you can safely manipulate the view, regardless if the thread of getSearchResultByBarcode() changes in the future. However will not hardcode the Scheduler directly, rather lets inject it in the presenter class, for example via the constructor. Of course when you're creating the "real" presenter, you'd pass in AndroidSchedulers.mainThread():
public MyPresenter(, Scheduler observeScheduler) {
...
this.observeScheduler = observeScheduler;
}
....
#Override
public void onBarcodeReceived(final String barcode) {
view.showProgress();
if (!textUtil.isEmpty(barcode)) {
subscriptions.add(interactor.getSearchResultByBarcode(barcode)
.observeOn(observeScheduler)
.subscribe(subscriberForSearchResults(true)));
}
}
Then in your test, when constructing the Presenter you'd use Schedulers.immediate() (if you're using RxJava 1.x or Schedulers.trampoline() if you're using RxJava 2.x version. That should work without using any timeout()s in your Unit tests with Mockito ... after all you want them to run as fast as possible.
And one unrelated thing - you can use org.apache.commons.lang3.StringUtils as a substitution of android.text.TextUtils - it has roughly the same functionality but you won't need to mock it in your unit tests.
In order to wait for another thread to complete you can use this Mockito feature: verify with timeout.
verify(view, timeout(100)).updateView(any(PriceAndStockActivityViewModel.class));
Or use some means of thread synchronization like CountDownLatch. See example for Mockito here.

Testing okHttp requests with Robolectric - callbacks

I have a function that I want to test which runs in an okHttp callback. I'm trying to test it using Robolectrics but the callback is never executed. I presume that is because the test moves on after request without waiting for okHttp to return. So far I've tried:
ShadowLooper.pauseMainLooper();
Robolectric.flushBackgroundScheduler();
ShadowLooper.unPauseMainLooper();
but that didn't work. Any suggestions?
EDIT:
Here's an example of my code:
ApiClient.sendSomeDataToServer(data, callback);
Where ApiClient is a helper class containing okHttp client. sendSomeDataToServer API call looks something like this:
public static void sendSomeDataToServer(MyObject data, Callback callback){
final Request request = new Request.Builder()
.url(API_SOME_URL)
.post(RequestBody.create(JSON, myObject.getAsJson().toString()))
.build();
sHttpClient.newCall(request).enqueue(callback);
}
Where sHttpClient is an initialised OkHttpClient.
I can test the execution of above by forcing Thread.sleep(5000) inside my test code and providing custom callback. The code I'm trying to test is inside the callback. Any suggestions how I can test that? I really don't want to change the main code to fit the test framework - should be the other way round.
Lets assume you have next code. Interface:
#GET("/user/{id}/photo")
void listUsers(#Path("id") int id, Callback<Photo> cb);
Implementation:
public void fetchData() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("baseURL")
.build();
ClientInterface service = restAdapter.create(ClientInterface.class);
Callback<Photo> callback = new Callback<Photo>() {
#Override
public void success(Photo o, Response response) {
}
#Override
public void failure(RetrofitError retrofitError) {
}
};
service.listUsers(435, callback);
}
First of all you need to change service instantiation to service injection (as parameter or field). I will do it as parameter:
public void fetchData(ClientInterface clients) {
}
After this text is quite trivial:
#Test
public void checkThatServiceSuccessIsProcessed() {
ClientInterface mockedClients = mock(ClientInterface.class);
activity.fetchData(mockedClients);
// get callback
ArgumentCaptor<Callback<Photo>> captor = (ArgumentCaptor<Callback<Photo>>)ArgumentCaptor.forClass(Callback.class);
verify(mockedInterface).listUsers(anything(), captor.capture());
Callback<Photo> passedCallback = captor.value();
// run callback
callback.success(...);
// check your conditions
}
The used library for mocking and verifying is Mockito.
There will be one warning with captor instantiation because of generics but it fixable if you will use #Captor annotation instead of creating captor by hands.
The parameter injection is not perfect, especially for case of activities. This was used to simplify example. Consider for proper injection with library or without. I would encourage you to try Dagger for injections
You can use ArgumentCaptor (Mockito's class).
Reference:
http://www.mdswanson.com/blog/2013/12/16/reliable-android-http-testing-with-retrofit-and-mockito.html

How to implement a "fire-and-forget" async call in Android?

I am reviewing/cleaning up some Android code. The code did some "asynchronous" network and data operations using this pattern:
new Thread() { public void run() { { ... runOnUiThread( { .. } ) }
A lot. With all the ugly consequences (no error checks, Sleeps, boolean finish variables...).
Till now, I ended up using (few) AsyncTasks and (more) Loaders as replacements.
Now I have stumbled upon a couple of "fire-and-forget" network communications: the code (currently, a Thread, as always) calls a web service posting some data.
I do not need to know if the data was received, and I do not need to know if there was any error.
Which is the best way of doing this? A Runnable or AsyncTask (static nested class, so I do not "leak" a reference to "this" activity?)
Or is there something better (more "lightweight" and cleaner?)
If you don't need to know when task execution is finished then you definitely don't need AsyncTask at all. Therefore the most lightweight solution is just a Thread. You can use Executor which will create threads for you. To get rid of nested classes you could predefine tasks. Sample:
Predefined task:
public static class TaskA implements Runnable{
private int someParam;
public TaskA(int someParam) {
this.someParam = someParam;
}
#Override
public void run() {
//
}
}
Static Executor
public static class FireAndForgetExecutor{
private static Executor executor = Executors.newFixedThreadPool(5);
public static void exec(Runnable command){
executor.execute(command);
}
}
And usage:
FireAndForgetExecutor.exec(new TaskA(10));
PS don't forget that a Thread is connected to the GC root! So if you pass heavy object like an activity or a bitmap into the task it could lead to memory leak.

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();
}
}
}
}

Calling Android's BaseAdapter notifyDataSetChanged() from a listener callback method

I've been struggling with this for some time now. In my project there are several Activities that include a ListView and a custom adapter extending BaseAdapter. They also implement some interfaces which usually inform it that the data has been changed.
However, when I call the notifyDataSetChanged() from the listener/interface method implemented in my class, the ListView is not updated and none of the adapter methods are called (getCount(), getView()).
I know this has something to do with the fact where the notifyDataSetChanged() is called from, I mean which thread, but I don't quite understand it and couldn't find a straightforward explanation of this behavior.
As a workaround of this problem I use a Handler which calls a method periodically and in that method I look if a boolean 'needsToBeUpdated' value is true and I call the notifyDataSetChanged() method then. This is ugly, though, and I am sure there needs to be a way to do it asynchronously.
Any help would be appreciated.
Pseudo-code of what I'm talking about:
public class FriendsActivity extends Activity implements FriendsListener {
private ListView mListView;
private ArrayList<Friends> mFriendsList;
private FriendsAdapter mFriendsAdapter;
private boolean mNeedsToBeUpdated;
private Handler mListUpdateHandler;
private Runnable mListUpdateTask;
onCreate() {
initViews();
mFriendsAdapter = new FriendsAdapter(mFriendsList);
mListView.setAdapter(mFriendsAdapter);
SomeStaticClass.addFriendListener(this)
mNeedsToBeUpdated = false;
mListUpdateHandler = new Handler();
mListUpdateHandler.removeCallbacks(mListUpdateTask);
mListUpdateHandler.postDelayed(mListUpdateTask, 10000);
}
onListenerMethod() {
updateFriendsList();
mFriendsAdapter.updateDataSource(mFriendsList);
mFriendsAdapter.notifyDataSetChanged(); // THIS DOESN'T UPDATE THE VIEWS
mNeedsToBeUpdated = true;
}
protected void onResume() {
mListUpdateTask = new Runnable() {
public void run() {
if (mNeedsToBeUpdated ) {
updateFriendsList();
mFriendsAdapter.updateDataSource(mFriendsList);
mFriendsAdapter.notifyDataSetChanged(); // THIS UPDATES THE VIEWS
mListsRequireUpdating = false;
}
mListUpdateHandler.postDelayed(mListUpdateTask, 10000);
}
};
mListUpdateHandler = new Handler();
mListUpdateHandler.removeCallbacks(mListUpdateTask);
mListUpdateHandler.post(mListUpdateTask);
super.onResume();
}
EDIT: Of course it took me almost no time to find the answer once I posted this here...
I hate to do it but I put some more effort into looking for an answer and I think I found it thanks to this: http://developer.android.com/resources/articles/painless-threading.html - and this: http://developer.android.com/reference/android/os/Handler.html
The solution is painfully simple. It is not possible to change the UI in any other thread but the main/UI thread. That's why doing it in the callback methods doesn't work. However, creating a Handler in the UI Thread (e.g. in the onCreate() method) connects it to the UI Thread and can be later used to post events to this thread.
mListUpdateHandler = new Handler();
Then, an inner class implementing the Runnable interface is needed where the UI tweaking is done.
class UpdateListRunnable implements Runnable {
#Override
public void run() {
Log.i(LOGTAG, "UpdateListRunnable");
FriendsActivity.this.updateLists();
}
}
Finally, in the callback method we post the event with out UpdateListRunnable class to the main thread via the Handler:
#Override
public void entriesUpdated(Collection<String> entries) {
Log.i(LOGTAG, "entriesUpdated");
for (String entry : entries) {
Log.i(LOGTAG, "entry: " + entry);
}
mListUpdateHandler.post(new UpdateListRunnable());
}
Thanks to that the upadteLists() method is run in the UI thread and everything works like a charm.
I had the same problem with my application. The first solution was to use Handlers. The more elegant one is to use Loaders - check http://developer.android.com/guide/components/loaders.html!
The compatibility package for older android versions make them available for Android 1.6+

Categories

Resources