Android : Retrofit Callback success and failure executed asynchronously? - android

Today I was just doing some research on Retrofit by our very own Jake Wharton, so I did something like this
RetroClass.getClient().getSomeData(param, new Callback<Model>(){
#Override
public void failure(...){/*blah*/}
#Override
public void success(Model response, Response notUsed)
{
try
{
Thread.sleep(10000);
}
catch(Exception e){e.pST();}
}});
I expected a ANR, but the flow is executing fine, Jake Wharton mentioned in this post
Does Retrofit make network calls on main thread?
"As it states in the answer, if you use the second pattern (last argument as a Callback) the request is done asynchronously but the callback is invoked on the main thread. By default Retrofit uses a thread pool for these requests."
that the call back is executed on the main thread, whats happening here, any insights ? Why isnt Thread.sleep() not causing an ANR here...? Im baffled....

Yes by default for the Android platform the Callback Executor used is MainThreadExecutor.
Make sure you're not overriding the default implementation when creating your RestAdapter by doing something like this
RestAdapter restAdapter = new RestAdapter.Builder()
.setExecutors(new MyHttpExecuter(), new MyCallbackExecutor()) { // watch out for this
.build()
If you override the default Callback Executor by setting your own then you won't get the default behaviour.

Related

How to verify Mockito interaction with the view that happen on new thread other than the one which is running the test case?

I have a below scenario when setView is called then Presenter fetches some data over the network on a new thread. Test fails by giving this reason - Actually, there were zero interactions with this mock. But it should pass if interaction gets verified.
Testcase
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
verify(rateView,times(1)).showRate(new Rate());
}
Inside "ratePresenter.setView"
Call<UnoRate> call1 = ratesAPI.getUnoRate();
call1.enqueue(new Callback<UnoRate>() {
#Override
public void onResponse(Call<UnoRate> call,Response<UnoRate> response) {
UnoRate unoRate = response.body();
Rate rate = new Rate();
rate.setBuyRate(unoRate.getBuy());
rate.setSellRate(unoRate.getSell());
rate.setFee(0);
rateView.showRate(rate);
}
});
One very simple solution is to use Mockito's verification with timeout feature. This will retry the verification repeatedly up until the timeout, looking for the condition to pass at some point or another.
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
verify(rateView, timeout(100).times(1)).showRate(new Rate());
}
The docs, however, warn against it: "This feature should be used rarely - figure out a better way of testing your multi-threaded system." This is probably because you're introducing a new aspect--time--as a proxy for the thing you really want to check, which is that all of the queues have been processed. You could even imagine a busy enough VM where a conservative timeout could cause the test to flake in automated testing systems but that works fine on development machines.
If feasible, you could switch your ratesAPI to use a synchronous executor, or instead you could add methods needed to your API accessor to block the test thread until all calls have returned asynchronously:
#Test
public void checkUnoRate() {
ratePresenter.setView(rateView,Constants.UNO);
ratesAPI.flush(); // Implement this to perform a Thread.join on the callback thread,
// or otherwise wait until all callbacks have been called.
verify(rateView,times(1)).showRate(new Rate());
}
Or, to remove multithreading and external API interactions from your test, simulate the callback synchronously:
#Mock RatesAPI ratesApiMock;
#Mock Call<UnoRate> unoRateCallMock;
#Captor Callback<UnoRate> unoRateCallbackCaptor;
#Test
public void checkUnoRate() {
// Set up mock.
when(ratesApiMock.getUnoRate()).thenReturn(unoRateCallMock);
// Perform the action.
ratePresenter.setView(rateView,Constants.UNO);
// Verify nothing happens yet.
verify(rateView, never()).showRate(any());
// Capture and trigger the callback.
verify(unoRateCallMock).enqueue(unoRateCallbackCaptor.capture());
unoRateCallbackCaptor.getValue().onResponse(yourCall, yourResponse);
// Verify the asynchronous action.
verify(rateView,times(1)).showRate(new Rate());
}
As a side note, eventually you'll probably want to verify against a different parameter than new Rate(). Mockito compares via equals methods when not using Mockito matchers.

Is it possible to re-subscribe to a Retrofit 2 observable?

I'm using Retrofit 2 with RxAndroid, and I want to keep a request going during a config change. I thought I could do it with Observable.cache() as described in this blog post and others I've seen, but the following flow causes an InterruptedException.
Observable<Result<List<Post>>> request =
postService.index(page).cache();
Subscription subscribeOne = request.subscribe();
subscribeOne.unsubscribe();
Subscription subscribeTwo = request.subscribe();
I'm pretty sure the following code in the Retrofit source is responsible for cancelling the request when unsubscribe is called.
// Attempt to cancel the call if it is still in-flight on unsubscription.
subscriber.add(Subscriptions.create(new Action0() {
#Override public void call() {
call.cancel();
}
}));
Not unsubscribing makes everything work, but this could cause leaks. Has anyone managed to handle config changes with Retrofit 2? Is there a different approach I can use?
Thanks to a hint from /u/insane-cabbage, I managed to implement this with a BehaviourSubject (safely encapsulated in a presenter). Here's an example of the flow.
BehaviorSubject<String> subject = BehaviorSubject.create();
/** User loads view and network request begins */
Observable.just("value")
.delay(200, TimeUnit.MILLISECONDS)
.subscribeOn(Schedulers.newThread())
.subscribe(subject::onNext);
Subscription portraitSub = subject.subscribe(
s -> System.out.println("Portrait: " + s));
/** onDestroy() */
portraitSub.unsubscribe();
/** Rotating... */
Thread.sleep(300);
/** onRestoreInstanceState() **/
Subscription landscapeSub = subject.subscribe(
s -> System.out.println("Landscape: " + s));
/** Output */
> Landscape: value
I have a working example RxApp that uses AsyncSubject to implement cache for network request and the code shows how to subscribe to a pending request. I'm a bit confused with Rx subjects as on the other they seem pretty handy but on the other hand it's recommended that they are to be used only in very seldom cases e.g. To Use Subject Or Not To Use Subject?. Would be great if some one could explain what really is the problem if they're used like in my example.

Cancelling network call in AsyncTask

I'm trying to handle the situation where the user loses their data connection during an Http request in an AsyncTask. Currently the AsyncTask will never finish executing if this happens and the app just stalls. I can't even check isCancelled() because the Http request just stalls out on one function call, so I can't implement a loop or anything.
I'm using the following in the UI thread but I can't figure out how to check if the task has been cancelled from within the task:
task = new LoginTask();
task.execute(this, mUsername, mPassword);
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
#Override
public void run()
{
if (task != null && task.getStatus() == AsyncTask.Status.RUNNING)
{
task.cancel(true);
}
}
}, 5000);
Since KSoap 2 is written in Java, an option for you would be to abstract one of the classes and manually set a timeout on the HttpConnection.
Based on our prior discussion, you will probably have to override the method that doesn't set a timeout on the connection and replace it with your own method and a connection that times out. Should be managable thanks to KSoap being open source! Good luck.
In order to stop processing, you would need to do a check on isCancelled() inside doInBackground(). If there are any reasonable places to do so in your doInBackground, you can add an if-statement. Otherwise you should consider setting a timeout on the http connection.

How to better unit test Looper and Handler code on Android?

I use the android.os.Handler class to perform tasks on the background. When unit testing these, I call Looper.loop() to make the test thread wait for the background task thread to do its thing. Later, I call Looper.myLooper().quit() (also in the test thread), to allow the test thread to quit the loop and resume the testing logic.
It's all fine and dandy until I want to write more than one test method.
The problem is that Looper doesn't seem to be designed to allow quitting and restarting on the same thread, so I am forced to do all of my testing inside a single test method.
I looked into the source code of Looper, and couldn't find a way around it.
Is there any other way to test my Hander/Looper code? Or maybe some more test friendly way to write my background task class?
The source code for Looper reveals that Looper.myLooper().quit() enqueues a null message in the Message queue, which tells Looper that it is done processing messages FOREVER. Essentially, the thread becomes a dead thread at that point, and there is no way to revive it that I know of. You may have seen error messages when attempting to post messages to the
Handler after quit() is called to the effect "attempting to send message to dead thread". That is what that means.
This can actually be tested easily if you aren't using AsyncTask by introducing a second looper thread (other than the main one created for you implicitly by Android). The basic strategy then is to block the main looper thread using a CountDownLatch while delegating all your callbacks to the second looper thread.
The caveat here is that your code under test must be able to support using a looper other than the default main one. I would argue that this should be the case regardless to support a more robust and flexible design, and it is also fortunately very easy. In general, all that must be done is to modify your code to accept an optional Looper parameter and use that to construct your Handler (as new Handler(myLooper)). For AsyncTask, this requirement makes it impossible to test it with this approach. A problem that I think should be remedied with AsyncTask itself.
Some sample code to get you started:
public void testThreadedDesign() {
final CountDownLatch latch = new CountDownLatch(1);
/* Just some class to store your result. */
final TestResult result = new TestResult();
HandlerThread testThread = new HandlerThread("testThreadedDesign thread");
testThread.start();
/* This begins a background task, say, doing some intensive I/O.
* The listener methods are called back when the job completes or
* fails. */
new ThingThatOperatesInTheBackground().doYourWorst(testThread.getLooper(),
new SomeListenerThatTotallyShouldExist() {
public void onComplete() {
result.success = true;
finished();
}
public void onFizzBarError() {
result.success = false;
finished();
}
private void finished() {
latch.countDown();
}
});
latch.await();
testThread.getLooper().quit();
assertTrue(result.success);
}
I've stumbled in the same issue as yours. I also wanted to make a test case for a class that use a Handler.
Same as what you did, I use the Looper.loop() to have the test thread starts handling the queued messages in the handler.
To stop it, I used the implementation of MessageQueue.IdleHandler to notify me when the looper is blocking to wait the next message to come. When it happen, I call the quit() method. But again, same as you I got a problem when I make more than one test case.
I wonder if you already solved this problem and perhaps care to share it with me (and possibly others) :)
PS: I also would like to know how you call your Looper.myLooper().quit().
Thanks!
Inspired by #Josh Guilfoyle's answer, I decided to try to use reflection to get access to what I needed in order to make my own non-blocking and non-quitting Looper.loop().
/**
* Using reflection, steal non-visible "message.next"
* #param message
* #return
* #throws Exception
*/
private Message _next(Message message) throws Exception {
Field f = Message.class.getDeclaredField("next");
f.setAccessible(true);
return (Message)f.get(message);
}
/**
* Get and remove next message in local thread-pool. Thread must be associated with a Looper.
* #return next Message, or 'null' if no messages available in queue.
* #throws Exception
*/
private Message _pullNextMessage() throws Exception {
final Field _messages = MessageQueue.class.getDeclaredField("mMessages");
final Method _next = MessageQueue.class.getDeclaredMethod("next");
_messages.setAccessible(true);
_next.setAccessible(true);
final Message root = (Message)_messages.get(Looper.myQueue());
final boolean wouldBlock = (_next(root) == null);
if(wouldBlock)
return null;
else
return (Message)_next.invoke(Looper.myQueue());
}
/**
* Process all pending Messages (Handler.post (...)).
*
* A very simplified version of Looper.loop() except it won't
* block (returns if no messages available).
* #throws Exception
*/
private void _doMessageQueue() throws Exception {
Message msg;
while((msg = _pullNextMessage()) != null) {
msg.getTarget().dispatchMessage(msg);
}
}
Now in my tests (which need to run on the UI thread), I can now do:
#UiThreadTest
public void testCallbacks() throws Throwable {
adapter = new UpnpDeviceArrayAdapter(getInstrumentation().getContext(), upnpService);
assertEquals(0, adapter.getCount());
upnpService.getRegistry().addDevice(createRemoteDevice());
// the adapter posts a Runnable which adds the new device.
// it has to because it must be run on the UI thread. So we
// so we need to process this (and all other) handlers before
// checking up on the adapter again.
_doMessageQueue();
assertEquals(2, adapter.getCount());
// remove device, _doMessageQueue()
}
I'm not saying this is a good idea, but so far it's been working for me. Might be worth trying out! What I like about this is that Exceptions that are thrown inside some hander.post(...) will break the tests, which is not the case otherwise.

Need an example showing how to do async HTTP requests

Im using a web service, so I want to use an async thread for the HTTP authentication request and another thread later to make additional service requests while my main thread runs.
Would like to see a good example of how to do this and how to show busy messages somehow in main app. How does the main app know when the thread finished? And what if my thread encounters exceptions, how do I deal with that?
HTTP requests are sent later, use the same cookies setup up by the first auth request, so will the later requests pick up the same cookies and just work?
Even better look here for the async part: Is there an accepted best-practice on making asynchronous HTTP requests in Android?
Look at here How to execute web request in its own thread?
AndroidAsync library I wrote to handle this automatically, it will run in the background and reinvoke onto the UI thread:
https://github.com/koush/AndroidAsync
// url is the URL to download. The callback will be invoked on the UI thread
// once the download is complete.
AsyncHttpClient.getDefaultInstance().get(url, new AsyncHttpClient.StringCallback() {
// Callback is invoked with any exceptions/errors, and the result, if available.
#Override
public void onCompleted(Exception e, String result) {
if (e != null) {
e.printStackTrace();
return;
}
System.out.println("I got a string: " + result);
}
});

Categories

Resources