How to unit test an async interactor? - java

I am completely new in junit testing, and I need to test a interactor with some async calls. My problem is that I do not understand what do I need to test without breaking the unity of the test.
If I test a pure call to interactor.execute(), I am testing other interactors functionalities too. I could mock interactor dependencies but they are inyected. I know it is a conceptual misunderstood, it must be much easier than I think.
public class NewLoginInteractorImpl implements NewLoginInteractor {
private final GetRegistrationIdInteractor getRegistrationIdInteractor;
private final GetDeviceIdInteractor getDeviceIdInteractor;
private final GetAndroidOSVersionInteractor getAndroidOSVersionInteractor;
private final NewLoginWebService newLoginWebService;
private static final String TAG = "NewLoginInteractorImpl";
#Inject
public NewLoginInteractorImpl(GetRegistrationIdInteractor getRegistrationIdInteractor, GetDeviceIdInteractor getDeviceIdInteractor, GetAndroidOSVersionInteractor getAndroidOSVersionInteractor, NewLoginWebService newLoginWebService) {
this.getRegistrationIdInteractor = getRegistrationIdInteractor;
this.getDeviceIdInteractor = getDeviceIdInteractor;
this.getAndroidOSVersionInteractor = getAndroidOSVersionInteractor;
this.newLoginWebService = newLoginWebService;
}
#Override
public void execute(final Callback callback) {
final String deviceId = getDeviceIdInteractor.execute();
final String os_version = getAndroidOSVersionInteractor.execute();
getRegistrationIdInteractor.execute(new GetRegistrationIdInteractor.Callback() {
#Override
public void onSuccess(String regid) {
NoLoginUser noLoginUser = new NoLoginUser(deviceId, regid, os_version);
callNoLoginWebService(noLoginUser, callback);
}
#Override
public void onFailure(String error) {
callback.onFailureGettingRegistrationId(error);
}
#Override
public void onRecoverableError(int resultCode, int playServicesResolutionRequest) {
callback.onRecoverableError(resultCode, playServicesResolutionRequest);
}
});
}
private void callNoLoginWebService(NoLoginUser noLoginUser, final Callback callback) {
newLoginWebService.noLogin(noLoginUser, new NewLoginWebService.Callback() {
#Override
public void onNoLoginFailure(String errorText) {
callback.onNoLoginFailure(errorText);
}
#Override
public void onNoLoginInvalidValues(JSONObject error) {
callback.onFailureGettingRegistrationId("ERROR");
}
#Override
public void onNoLoginSuccess(JSONObject response) {
callback.onSuccess(response);
}
#Override
public void onNullResponse() {
callback.onNullResponse();
}
});
}
}

Related

How to return something from a callback function to parent function

This is what I got to do
UploadCompleteListener is a custom interface that acts as a callback.
#Overrider
public Result doWork() {
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
return Result.success(); //this is what I want to do
}
#Override
public void uploadFailed(String reason) {
return Result.failure(); //this is what I want to do
}
});
return null;
}
Is it Possible?
If possible in any way please response soon. I can provide more details if you need it.
** This is what worked for me **
#NonNull
#Override
public Result doWork() {
final Result[] result = new Result[1];
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
result[0] = Result.success(); //this is what I want to do
}
#Override
public void uploadFailed(String reason) {
result[0] = Result.failure(); //this is what I want to do
}
});
return result[0];
}
public Result doWork(UpLoadDataService.UploadCompleteListener uploadListener) {
mUpLoadDataService.uploadInspectionData(uploadListener);
return null;
}
now pass the implementation from parent function. Lets say your parent function is named foobar
void foobar() {
someObject.doWork(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
//write your logic here
return Result.success();
}
#Override
public void uploadFailed(String reason) {
//write your logic here
return Result.failure();
}
});
}
You have to realize that you are trying to get synchronously a result from an asynchronous call. This call is asynchronous for a reason, so the short answer is no, you can't.
Instead of returning a Result, you could return, for instance, a Future, which models the asynchronicity of the operation.
For android take a look at CallbackToFutureAdapter
https://developer.android.com/reference/kotlin/androidx/concurrent/futures/CallbackToFutureAdapter
https://developer.android.com/reference/java/util/concurrent/Future
You could use EventBus to notify Subscribed methods in every place you want like so:
public class MessageEvent {
public final String message;
public MessageEvent(String message) {
this.message = message;
}
}
public void doWork() {
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
EventBus.getDefault().post(new MessageEvent("success"));
}
#Override
public void uploadFailed(String reason) {
EventBus.getDefault().post(new MessageEvent("failed"));
}
});
}
#Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MessageEvent event) {
Toast.makeText(getActivity(), event.message, Toast.LENGTH_SHORT).show();
}
See this implementation guide.
You can get the callback of your task using the following way
doWork(paremter, new ServiceListener<String>() { //paremter if any
#Override
public void success(String obj) {
//get the response if success
}
#Override
public void fail(ServiceError error) {
//get the error response
}
});
do the work and send the call response from where it called
private void doWork(String param , ServiceListener<String> serviceListener) {
mUpLoadDataService.uploadInspectionData(new UpLoadDataService.UploadCompleteListener() {
#Override
public void uploadComplete() {
serviceListener.success("success");
}
#Override
public void uploadFailed(String reason) {
serviceListener.fail(new ServiceError("Can not Upload"));
}
});
}
ServiceListener interface will be defined as follow
public interface ServiceListener<T> {
void success(T obj);
void fail(ServiceError error);
}
public class ServiceError {
public Throwable errorObject;
public String message;
public ServiceError(){
message = "";
}
public ServiceError(String message){
this.message = message;
}
public ServiceError(String message, Throwable errorObject){
this.message = message;
this.errorObject = errorObject;
}
public Object getErrorObject() {
return errorObject;
}
public void setErrorObject(Throwable errorObject) {
this.errorObject = errorObject;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}

Unit Testing In Network With Using Volley in Android

I am trying to test functions from my Presenter Class. In below, I can reach getSomeThing() function however, I cannot reach the getData() and getError() functions.
Volley functions are not working in unit tests. Further help would be highly appreciated as I am struggling with this for over a week.
Below is my Presenter Class, Presenter Listener and Test Function.
my Presenter Class:
public abstract class SomePresenter implements BasePresenterListener {
private static final String sTAG = SomePresenter.class.getSimpleName();
private Context context;
private Integer testInteger;
protected SomePresenter(Context context, Integer testInteger) {
this.context = context;
this.testInteger = testInteger;
onResponse();
}
#Override
public void onResponse() {
try {
Thread.sleep(1000);
getSomeThing();
} catch (InterruptedException e) {
e.printStackTrace();
}
final GetRequest<SomeResponse> someResponseRequest =
ApiRequests.getSomeResponse(
new Response.Listener<SomeResponse>() {
#Override
public void onResponse(SomeResponse response) {
getData(response);
}
}
,
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
ErrorResponse errorResponse = new ErrorResponse();
errorResponse.setMessage(Constant.NETWORK_ERROR);
getError(errorResponse);
}
}
);
NetworkInstance.addRequest(context, poiResponseGetRequest, sTAG);
}
public static void cancelRequest(Context context) {
NetworkInstance.cancelAllRequests(context, sTAG);
}
protected abstract void getSomeThing();
protected abstract void getData(SomeResponse response);
protected abstract void getError(ErrorResponse response);
}
my BasePresenter Class:
public interface BasePresenterListener {
void onResponse();
}
my Unit Test Function:
#Test
public void test() throws InterruptedException {
new SomePresenter(mockContext, 107){
#Override
protected void getData(PoiResponse response) {
SomeLogger.debug("getData works");//this is not called.
}
#Override
protected void getSomeThing() {
SomeLogger.debug("getSomeThing works!");//this is called.
}
#Override
protected void getError(ErrorResponse response) {
SomeLogger.debug("ErrorResponse works!"); //this is not called.
}
};
}
I have looked below links none of them solved my problem.
Unit testing a network response. Works when debugging, not when actually running
Android Unit test with Volley
In my case is I can reach the getSomething() function from unit test but I cannot reach the getData() nor getError() functions because Volley does not seem to work in unit tests.
All, getSomething(), getData(), getError() functions are callback functions, I can reach the getSomeThing() function but I cannot reach the getData() and getError() functions.

Android Espresso IdlingPolicies.setIdlingResourceTimeout not working

I have a few classes to test deep links. Each class test 1 deep link.
There are few tests that always terminate correctly at 10+ seconds when I run them individually but always take more than 45 seconds to finish when I run them together.
Note that all these tests are supposed to fail all the times now (I haven't done the implementation for them) so the results should not vary.
Am I using IdlingPolicies.setIdlingResourceTimeout wrong?
My test class:
#RunWith(AndroidJUnit4.class)
public class DeepLinkActivityBagTest extends MyActivityTest {
#Rule
public MyActivityTestRule<DeepLinkActivity> activityTestRule =
new MyActivityTestRule<DeepLinkActivity>(DeepLinkActivity.class) {
#Override
protected Intent getActivityIntent() {
Intent intent = super.getActivityIntent();
intent.setAction(Intent.ACTION_VIEW);
intent.setData(Uri.parse("xxx://bag"));
return intent;
}
};
#Test
public void testCorrectScreenOpened() {
setUpWait();
String expectedToolbarTitle = "shopping bag" + mToolbarTitleSuffix;
onView(withTextIgnoreCase(expectedToolbarTitle)).check(matches(isDisplayed()));
}
}
Its parent class:
#RunWith(AndroidJUnit4.class)
public class MyActivityTest {
#Rule
public MyActivityTestRule<DeepLinkActivity> activityTestRule;
protected String mToolbarTitleSuffix;
#Before
public void prepare() {
if (!BuildConfig.FLAVOR.equals(ENV_PRODUCTION)) {
mToolbarTitleSuffix = String.format(" (%s)", BuildConfig.FLAVOR);
}
}
#After
public void resetCountry() {
if (activityTestRule != null) {
PrefUtils.clear(InstrumentationRegistry.getTargetContext());
PrefUtils.setCurrentCountryCode(activityTestRule.getmCurCountryCode());
PrefUtils.setCurrentLangCode(activityTestRule.getmCurLangCode());
}
}
protected void setUpWait() {
try {
Thread.sleep(3 * 1000);
} catch (InterruptedException e) {
Logger.e(e, "");
}
IdlingPolicies.setMasterPolicyTimeout(10, TimeUnit.SECONDS);
IdlingPolicies.setIdlingResourceTimeout(5, TimeUnit.SECONDS);
}
}
MyActivityTestRule:
public class MyActivityTestRule<D extends Activity> extends ActivityTestRule {
private String mCurCountryCode;
private String mCurLangCode;
protected MyActivityTestRule(Class activityClass) {
super(activityClass);
}
public MyActivityTestRule(Class activityClass, boolean initialTouchMode) {
super(activityClass, initialTouchMode);
}
public MyActivityTestRule(Class activityClass, boolean initialTouchMode, boolean launchActivity) {
super(activityClass, initialTouchMode, launchActivity);
}
#Override
protected void beforeActivityLaunched() {
super.beforeActivityLaunched();
PrefUtils.setup(InstrumentationRegistry.getTargetContext());
mCurCountryCode = PrefUtils.getCurrentCountryCode();
mCurLangCode = PrefUtils.getCurrentLangCode();
PrefUtils.clear(InstrumentationRegistry.getTargetContext());
PrefUtils.setCurrentCountryCode("SG");
PrefUtils.setCurrentLangCode("en");
}
String getmCurCountryCode() {
return mCurCountryCode;
}
String getmCurLangCode() {
return mCurLangCode;
}
}

How to properly convert Listeners to Reactive (Observables) using RxJava?

I'm using a multiplayer Game Client that's called AppWarp (http://appwarp.shephertz.com), where you can add event listeners to be called back when event's happen, let's assume we'll be talking about the Connection Listener, where you need to implement this interface:
public interface ConnectionRequestListener {
void onConnectDone(ConnectEvent var1);
void onDisconnectDone(ConnectEvent var1);
void onInitUDPDone(byte var1);
}
My goal here is to mainly create a Reactive version of this client to be used in my Apps Internally instead of using the Client itself directly (I'll also rely on interfaces later instead of just depending on the WarpClient itself as in the example, but that's not the important point, please read my question at the very end).
So what I did is as follows:
1) I introduced a new event, named it RxConnectionEvent (Which mainly groups Connection-Related events) as follows:
public class RxConnectionEvent {
// This is the original connection event from the source client
private final ConnectEvent connectEvent;
// this is to identify if it was Connection / Disconnection
private final int eventType;
public RxConnectionEvent(ConnectEvent connectEvent, int eventType) {
this.connectEvent = connectEvent;
this.eventType = eventType;
}
public ConnectEvent getConnectEvent() {
return connectEvent;
}
public int getEventType() {
return eventType;
}
}
2) Created some event types as follows:
public class RxEventType {
// Connection Events
public final static int CONNECTION_CONNECTED = 20;
public final static int CONNECTION_DISCONNECTED = 30;
}
3) Created the following observable which emits my new RxConnectionEvent
import com.shephertz.app42.gaming.multiplayer.client.WarpClient;
import com.shephertz.app42.gaming.multiplayer.client.events.ConnectEvent;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action0;
import rx.subscriptions.Subscriptions;
public class ConnectionObservable extends BaseObservable<RxConnectionEvent> {
private ConnectionRequestListener connectionListener;
// This is going to be called from my ReactiveWarpClient (Factory) Later.
public static Observable<RxConnectionEvent> createConnectionListener(WarpClient warpClient) {
return Observable.create(new ConnectionObservable(warpClient));
}
private ConnectionObservable(WarpClient warpClient) {
super(warpClient);
}
#Override
public void call(final Subscriber<? super RxConnectionEvent> subscriber) {
subscriber.onStart();
connectionListener = new ConnectionRequestListener() {
#Override
public void onConnectDone(ConnectEvent connectEvent) {
super.onConnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_CONNECTED));
}
#Override
public void onDisconnectDone(ConnectEvent connectEvent) {
super.onDisconnectDone(connectEvent);
callback(new RxConnectionEvent(connectEvent, RxEventType.CONNECTION_DISCONNECTED));
}
// not interested in this method (for now)
#Override
public void onInitUDPDone(byte var1) { }
private void callback(RxConnectionEvent rxConnectionEvent)
{
if (!subscriber.isUnsubscribed()) {
subscriber.onNext(rxConnectionEvent);
} else {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
};
warpClient.addConnectionRequestListener(connectionListener);
subscriber.add(Subscriptions.create(new Action0() {
#Override
public void call() {
onUnsubscribed(warpClient);
}
}));
}
#Override
protected void onUnsubscribed(WarpClient warpClient) {
warpClient.removeConnectionRequestListener(connectionListener);
}
}
4) and finally my BaseObservable looks like the following:
public abstract class BaseObservable<T> implements Observable.OnSubscribe<T> {
protected WarpClient warpClient;
protected BaseObservable (WarpClient warpClient)
{
this.warpClient = warpClient;
}
#Override
public abstract void call(Subscriber<? super T> subscriber);
protected abstract void onUnsubscribed(WarpClient warpClient);
}
My question is mainly: is my implementation above correct or should I instead create separate observable for each event, but if so, this client has more than 40-50 events do I have to create separate observable for each event?
I also use the code above as follows (used it in a simple "non-final" integration test):
public void testConnectDisconnect() {
connectionSubscription = reactiveWarpClient.createOnConnectObservable(client)
.subscribe(new Action1<RxConnectionEvent>() {
#Override
public void call(RxConnectionEvent rxEvent) {
assertEquals(WarpResponseResultCode.SUCCESS, rxEvent.getConnectEvent().getResult());
if (rxEvent.getEventType() == RxEventType.CONNECTION_CONNECTED) {
connectionStatus = connectionStatus | 0b0001;
client.disconnect();
} else {
connectionStatus = connectionStatus | 0b0010;
connectionSubscription.unsubscribe();
haltExecution = true;
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
fail("Unexpected error: " + throwable.getMessage());
haltExecution = true;
}
});
client.connectWithUserName("test user");
waitForSomeTime();
assertEquals(0b0011, connectionStatus);
assertEquals(true, connectionSubscription.isUnsubscribed());
}
I suggest you avoid extending the BaseObservable directly since it's very error prone. Instead, try using the tools Rx itself gives you to create your observable.
The easiest solution is using a PublishSubject, which is both an Observable and a Subscriber. The listener simply needs to invoke the subject's onNext, and the subject will emit the event. Here's a simplified working example:
public class PublishSubjectWarpperDemo {
public interface ConnectionRequestListener {
void onConnectDone();
void onDisconnectDone();
void onInitUDPDone();
}
public static class RxConnectionEvent {
private int type;
public RxConnectionEvent(int type) {
this.type = type;
}
public int getType() {
return type;
}
public String toString() {
return "Event of Type " + type;
}
}
public static class SimpleCallbackWrapper {
private final PublishSubject<RxConnectionEvent> subject = PublishSubject.create();
public ConnectionRequestListener getListener() {
return new ConnectionRequestListener() {
#Override
public void onConnectDone() {
subject.onNext(new RxConnectionEvent(1));
}
#Override
public void onDisconnectDone() {
subject.onNext(new RxConnectionEvent(2));
}
#Override
public void onInitUDPDone() {
subject.onNext(new RxConnectionEvent(3));
}
};
}
public Observable<RxConnectionEvent> getObservable() {
return subject;
}
}
public static void main(String[] args) throws IOException {
SimpleCallbackWrapper myWrapper = new SimpleCallbackWrapper();
ConnectionRequestListener listner = myWrapper.getListener();// Get the listener and attach it to the game here.
myWrapper.getObservable().observeOn(Schedulers.newThread()).subscribe(event -> System.out.println(event));
listner.onConnectDone(); // Call the listener a few times, the observable should print the event
listner.onDisconnectDone();
listner.onInitUDPDone();
System.in.read(); // Wait for enter
}
}
A more complex solution would be to use one of the onSubscribe implementations to create an observable using Observable.create(). For example AsyncOnSubscibe. This solution has the benefit of handling backperssure properly, so your event subscriber doesn't become overwhelmed with events. But in your case, that sounds like an unlikely scenario, so the added complexity is probably not worth it.

Retrying the request using Retrofit 2

How can I add retry functionality to the requests sent by Retrofit 2 library. Something like:
service.listItems().enqueue(new Callback<List<Item>>() {
#Override
public void onResponse(Response<List<Item>> response) {
...
}
#Override
public void onFailure(Throwable t) {
...
}
}).retryOnFailure(5 /* times */);
I finally did something like this, for anyone interested:
1
First I made an abstract class CallbackWithRetry
public abstract class CallbackWithRetry<T> implements Callback<T> {
private static final int TOTAL_RETRIES = 3;
private static final String TAG = CallbackWithRetry.class.getSimpleName();
private final Call<T> call;
private int retryCount = 0;
public CallbackWithRetry(Call<T> call) {
this.call = call;
}
#Override
public void onFailure(Throwable t) {
Log.e(TAG, t.getLocalizedMessage());
if (retryCount++ < TOTAL_RETRIES) {
Log.v(TAG, "Retrying... (" + retryCount + " out of " + TOTAL_RETRIES + ")");
retry();
}
}
private void retry() {
call.clone().enqueue(this);
}
}
Using this class I can do something like this:
serviceCall.enqueue(new CallbackWithRetry<List<Album>>(serviceCall) {
#Override
public void onResponse(Response<List<Album>> response) {
...
}
});
2
This is not completely satisfactory because I have to pass same serviceCall twice. This can confusing as one can think the second serviceCall (that goes into constructor of CallbackWithRetry) should or could be something different from first one (which we invoke enqueue method on it)
So I implemented a helper class CallUtils:
public class CallUtils {
public static <T> void enqueueWithRetry(Call<T> call, final Callback<T> callback) {
call.enqueue(new CallbackWithRetry<T>(call) {
#Override
public void onResponse(Response<T> response) {
callback.onResponse(response);
}
#Override
public void onFailure(Throwable t) {
super.onFailure(t);
callback.onFailure(t);
}
});
}
}
And I can use it like this:
CallUtils.enqueueWithRetry(serviceCall, new Callback<List<Album>>() {
#Override
public void onResponse(Response<List<Album>> response) {
...
}
#Override
public void onFailure(Throwable t) {
// Let the underlying method do the job of retrying.
}
});
With this I have to pass a standard Callback to enqueueWithRetry method and it makes me implement onFailure (Though in the previous method I can implement it too)
So this is how I've solved the issue. Any suggestion for a better design would be appreciated.
I've made custom implementation of the Callback interface, you can pretty much use it in place of original callback. If call is successful, the onResponse() method is called. If after retrying for set amount of repetitions call fails, onFailedAfterRetry() is called.
public abstract class BackoffCallback<T> implements Callback<T> {
private static final int RETRY_COUNT = 3;
/**
* Base retry delay for exponential backoff, in Milliseconds
*/
private static final double RETRY_DELAY = 300;
private int retryCount = 0;
#Override
public void onFailure(final Call<T> call, Throwable t) {
retryCount++;
if (retryCount <= RETRY_COUNT) {
int expDelay = (int) (RETRY_DELAY * Math.pow(2, Math.max(0, retryCount - 1)));
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
retry(call);
}
}, expDelay);
} else {
onFailedAfterRetry(t);
}
}
private void retry(Call<T> call) {
call.clone().enqueue(this);
}
public abstract void onFailedAfterRetry(Throwable t);
}
https://gist.github.com/milechainsaw/811c1b583706da60417ed10d35d2808f
ashkan-sarlak answer work great and i'm just try to make it up to date.
From retrofit 2.1
onFailure(Throwable t)
Change to
onFailure(Call<T> call, Throwable t)
So this make it so easy now.just create CallbackWithRetry.java like this
public abstract class CallbackWithRetry<T> implements Callback<T> {
private static final int TOTAL_RETRIES = 3;
private static final String TAG = CallbackWithRetry.class.getSimpleName();
private int retryCount = 0;
#Override
public void onFailure(Call<T> call, Throwable t) {
Log.e(TAG, t.getLocalizedMessage());
if (retryCount++ < TOTAL_RETRIES) {
Log.v(TAG, "Retrying... (" + retryCount + " out of " + TOTAL_RETRIES + ")");
retry(call);
}
}
private void retry(Call<T> call) {
call.clone().enqueue(this);
}
}
That's all! you can simply use it like this
call.enqueue(new CallbackWithRetry<someResponseClass>() {
#Override
public void onResponse(#NonNull Call<someResponseClass> call, #NonNull retrofit2.Response<someResponseClass> response) {
//do what you want
}
#Override
public void onFailure(#NonNull Call<someResponseClass> call, #NonNull Throwable t) {
super.onFailure(call,t);
//do some thing to show ui you trying
//or don't show! its optional
}
});
Go with RxJava Observable and call retry()
Doc: https://github.com/ReactiveX/RxJava/wiki/Error-Handling-Operators
I did something quite similar to Ashkan Sarlak, but since Retrofit 2.1 passes the Call<T> into the onFailure method, you can simplify to one CallbackWithRetry<T> abstract class. See:
public abstract class CallbackWithRetry<T> implements Callback<T> {
private static final String TAG = "CallbackWithRetry";
private int retryCount = 0;
private final Logger logger;
private final String requestName;
private final int retryAttempts;
protected CallbackWithRetry(#NonNull Logger logger, #NonNull String requestName, int retryAttempts) {
this.logger = logger;
this.requestName = requestName;
this.retryAttempts = retryAttempts;
}
#Override
public void onFailure(Call<T> call, Throwable t) {
if (retryCount < retryAttempts) {
logger.e(TAG, "Retrying ", requestName, "... (", retryCount, " out of ", retryAttempts, ")");
retry(call);
retryCount += 1;
} else {
logger.e(TAG, "Failed request ", requestName, " after ", retryAttempts, " attempts");
}
}
private void retry(Call<T> call) {
call.clone().enqueue(this);
}
}
With Retrofit 2.5
Now it's possible to make async sync calls through java.util.concurrent.CompletableFuture, the code waits for it's completion wich is very nice.
Here's a gist with a working solution.
Another solution for this problem if retry is optional :
public class CustomCallback<T> implements Callback<T> {
#NonNull
Callback<T> callback;
private int retryCount = 0;
private int maxRetry = 0;
#EverythingIsNonNull
public CustomCallback(Callback<T> callback) {
this.callback = callback;
}
public CustomCallback<T> retryOnFailure(int nbRetry) {
maxRetry = nbRetry;
return this;
}
#EverythingIsNonNull
#Override
public void onResponse(Call<T> call, Response<T> response) {
callback.onResponse(call, response);
}
#EverythingIsNonNull
#Override
public void onFailure(Call<T> call, Throwable t) {
if (maxRetry > retryCount) {
retryCount++;
call.clone().enqueue(this);
return;
}
callback.onFailure(call, t);
}
}
This way, you can choose if you want retry or not :
//With retry
myAPI.makeCall().enqueue(new CustomCallback<>(myCallback).retryOnFailure(3));
//Without
myAPI.makeCall().enqueue(new CustomCallback<>(myCallback));
I think for android we no need to go for retrofit for this.We can make use of Workmanager (which predefine android api).
We can use "ListenableWorker.Result.SUCCESS","ListenableWorker.Result.RETRY" ,etc and achieve the above goals.

Categories

Resources