How to do retrofti calls with OOP? - java

Today i was working on my homework, which it was making simple apps with retrofit calls and learning new things for code improvement, and somehow i saw there are so many ways to write less code and do better with OOP. So to improve my code experiment I'm trying to do my retrofit calls with OOP. So this is my issue right now:
Consider a simple retrofit call with CompositeDisposable( I'm developing my simples with MVP ) :
mView.showProgress(1);
RequestRemainingProductsRequest requestRemainingProductsRequest = new RequestRemainingProductsRequest();
requestRemainingProductsRequest.distributorId = distributorId;
requestRemainingProductsRequest.requestCode = requestCode;
requestRemainingProductsRequest.requestType = 1;
NetworkCalls.getObservableList();
compositeDisposable.add(getApi().requestRemainingProducts(requestRemainingProductsRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Products>>() {
#Override
public void accept(List<Products> products) throws Throwable {
mView.hideProgress(1);
mView.getRemainingProducts(products);
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
mView.hideProgress(1);
mView.showLog(throwable.getMessage().toString());
}
}));
And, Another retrofit call without CompositeDisposable :
ProductSellerRequest productSellerRequest = new ProductSellerRequest();
productSellerRequest.centralId = centralsList.get(i).requestCentralId;
productSellerRequest.requestType = 0;
productSellerRequest.productId = Constant.currentProduct.productId;
getApi().checkProductExistInRequest(productSellerRequest)
.enqueue(new Callback<ProductSellerCallback>() {
#Override
public void onResponse(Call<ProductSellerCallback> call, Response<ProductSellerCallback> response) {
hideProgress(myViewHolder);
showAddDialog(myViewHolder, v, response, i);
}
#Override
public void onFailure(Call<ProductSellerCallback> call, Throwable t) {
hideProgress(myViewHolder);
}
});
So let's say I created a java class with NetworkCalls.java, and created 2 voids like this:
public static void getObservableList()
{
}
public static void getWithoutObservableList()
{
}
How to handle my response to return to my Presenter/Activity?
This is how i using StringBuilder and returning my String, but I'm trying do similiar way to make repository for my Network Calls, then learn all all i should know about Repository Pattern.
public static String TotalPriceStringBuilder(int Price){
String DecimalPrice = String.format("%,d", Price);
String FinalString = new StringBuilder("Price : ").append(DecimalPrice).append(" $").toString();
return String.valueOf(FinalString);
}
This is what I've tried, but i still don't know how to fix it or make it work, what to return, and how to return and etc... :
private static ApiClient mApi;
private List<Products> receivedProducts;
private int hideProgress;
private boolean status;
private String message;
public void getObservableList(RequestRemainingProductsRequest requestRemainingProductsRequest, CompositeDisposable compositeDisposable)
{
compositeDisposable.add(getApi().requestRemainingProducts(requestRemainingProductsRequest)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<List<Products>>() {
#Override
public void accept(List<Products> products) throws Throwable {
hideProgress = 1;
receivedProducts = products;
status = TRUE;
}
}, new Consumer<Throwable>() {
#Override
public void accept(Throwable throwable) throws Exception {
hideProgress = 1;
status = FALSE;
message = throwable.getMessage().toString();
}
}));
if (status == TRUE) {
return hideProgress, receivedProducts, status;
} else {
return hideProgress, message, status;
}
}
public ApiClient getApi() {
if (mApi == null) {
mApi = ApiService.getClient().create(ApiClient.class);
}
return mApi;
}
If i use static method I'll get bunch of errors like can't be refrenced from a static context or etc...

Related

Retrofit call contains error Required retrofit2.Call, found void

Hello I recently started retrofit While implementing the code im getting this error
Incompatible types.
Required: retrofit2.Call <java.util.List<com.my.package.Youtube.YoutubePost>>
Found: void
I was trying to get YouTube channel playlist using Youtube v3 API
This is my code for the same
YoutubeActivity
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(ScalarsConverterFactory.create())
.baseUrl(AppConstant.API_YT_BASE)
.addConverterFactory(GsonConverterFactory.create())
.build();
YoutubeApiInterface youtubeApiInterface = retrofit.create(YoutubeApiInterface.class);
Call<List<YoutubePost>> call = youtubeApiInterface.getPlayList().enqueue(new Callback<List<YoutubePost>>() {
#Override
public void onResponse(Call<List<YoutubePost>> call, Response<List<YoutubePost>> response) {
if (response.isSuccessful()) {
} else {
}
}
#Override
public void onFailure(Call<List<YoutubePost>> call, Throwable t) {
t.printStackTrace();
}
});
YoutubePost
public class YoutubePost implements Parcelable {
#SerializedName("items")
private List<YoutubeItems> ytItems = new ArrayList<>();
private String nextPageToken;
public List<YoutubeItems> getYtItems() {
return ytItems;
}
public String getNextPageToken() {
return nextPageToken;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(ytItems);
dest.writeString(nextPageToken);
}
protected YoutubePost(Parcel in) {
ytItems = in.readParcelable(YoutubeItems.class.getClassLoader());
nextPageToken = in.readString();
}
public static final Creator<YoutubePost> CREATOR = new Creator<YoutubePost>() {
#Override
public YoutubePost createFromParcel(Parcel source) {
return new YoutubePost(source);
}
#Override
public YoutubePost[] newArray(int size) {
return new YoutubePost[size];
}
};
}
Screenshot of error message attached below
I suggest you, to modify the getPlayList method a little bit, to return with a Call, then break the lines.
Without your YoutubeApiInterface this is all I got.
Call<List<YoutubePost>> call = youtubeApiInterface.getPlayList();
call.enqueue(new Callback<List<YoutubePost>>() {
#Override
public void onResponse(Call<List<YoutubePost>> call, Response<List<YoutubePost>> response) {
if (response.isSuccessful()) {
} else {
}
}.
#Override
public void onFailure(Call<List<YoutubePost>> call, Throwable t) {
t.printStackTrace();
}
});
By this you won't have a type problem, because the call itself will be modified, during the enqueue.

Java andorid RXJava2 post in service to Activity and have to times this same onNext

In service I post my event :
RxBus.getSubject().onNext(eventAddNoteAndRealize) ;
This is my RxBus :
public final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public synchronized static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
}
And In my Activity I have this :
DisposableObserver<Object> disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
if (o instanceof EventAddNoteAndRealize) {
Toast.makeText(NewMainActivity.this , "next", Toast.LENGTH_LONG).show();
EventAddNoteAndRealize event = new EventAddNoteAndRealize(((EventAddNoteAndRealize) o).getNoteAndRealizeDAOList());
eventAddNoteAndRealize = event;
getRealizeAndNote((EventAddNoteAndRealize)o);
}
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
And a method onNext execute two times this same objects. I do not have idea what I did wrong
Are you sure that you are not emitting the same object 2 times because as i test your code it works good
#Override
public void run(String... args) throws Exception {
DisposableObserver<Object> disposable = RxBus.getSubject().
subscribeWith(new DisposableObserver<Object>() {
#Override
public void onNext(Object o) {
System.out.println(o);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Observable.interval(100, TimeUnit.MILLISECONDS)
.map(aLong -> {
RxBus.getSubject().onNext(aLong);
return aLong;
}).subscribe();
}
}
final class RxBus {
private static final BehaviorSubject<Object> behaviorSubject
= BehaviorSubject.create();
public synchronized static BehaviorSubject<Object> getSubject() {
return behaviorSubject;
}
make sure that you are not emitting object to times my response is
0
1
2
3
4
5
6
.
.
.

java.lang.NullPointerException Android Studio [duplicate]

This question already has answers here:
What is a NullPointerException, and how do I fix it?
(12 answers)
Closed 4 years ago.
I got this from a tutorial and made some changes but I'm unable to figure out why it's pointing to a null object.
Here are the codes:
HomeActivity.java
final DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference(Common.token_table).child(Common.user_workers_table).child(stringWorkerType);
dbRef.orderByKey().equalTo(workerId).addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
for (DataSnapshot ds: dataSnapshot.getChildren()) {
Token token = ds.getValue(Token.class);
//Make raw payload - convert LatLng to json
String json_lat_lng = new Gson().toJson(new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude()));
String workerToken = FirebaseInstanceId.getInstance().getToken();
Notification notification = new Notification(workerToken, json_lat_lng);
Sender content = new Sender(token.getToken(), notification);
//IFCMService mService;
mService.sendMessage(content).enqueue(new Callback<FCMResponse>() {
#Override
public void onResponse(Call<FCMResponse> call, Response<FCMResponse> response) {
if(response.body().success == 1) {
Log.d("LOG/I", "Request sent.");
} else {
Toast.makeText(HomeActivity.this, "Request not sent.", Toast.LENGTH_SHORT).show();
}
}
IFCMService.java
public interface IFCMService {
#Headers({
"Content-Type:application/json",
"Authorization:key=AAAAJorGt9o:APA91bFgAhEUL9oCFSD9wnLEflqw5hip6Q7kZ7E4JPX7mY5NLTb78lnvlbhMikojpa2Gp-2LnVE1pfXNhyXY25JFj-omR9_OgDN5qcj2rvqUeaYIolhi1uNKa2o3sErk-15PjojYEy7z"
})
#POST("fcm/send")
Call<FCMResponse> sendMessage(#Body Sender body);
}
FCMResponse.java
public class FCMResponse {
public long multicast_id;
public int success;
public int failure;
public int canonical_ids;
public List<Result> results;
public FCMResponse() {
}
public FCMResponse(long multicast_id, int success, int failure, int canonical_ids, List<Result> results) {
this.multicast_id = multicast_id;
this.success = success;
this.failure = failure;
this.canonical_ids = canonical_ids;
this.results = results;
}
public long getMulticast_id() {
return multicast_id;
}
public void setMulticast_id(long multicast_id) {
this.multicast_id = multicast_id;
}
public int getSuccess() {
return success;
}
public void setSuccess(int success) {
this.success = success;
}
public int getFailure() {
return failure;
}
public void setFailure(int failure) {
this.failure = failure;
}
public int getCanonical_ids() {
return canonical_ids;
}
public void setCanonical_ids(int canonical_ids) {
this.canonical_ids = canonical_ids;
}
public List<Result> getResults() {
return results;
}
public void setResults(List<Result> results) {
this.results = results;
}
}
Sender.class
public class Sender {
public String to;
public Notification notification;
public Sender() {
}
public Sender(String to, Notification notification) {
this.to = to;
this.notification = notification;
}
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
public Notification getNotification() {
return notification;
}
public void setNotification(Notification notification) {
this.notification = notification;
}
}
It's an app like uber, what this codes supposed to be doing is when the driver/client app request and a driver/worker is available, it will give a notification to the driver/worker. But it does nothing and I'm getting an error at
java.lang.NullPointerException
at
com.fixitph.client.HomeActivity$22$1.onResponse(HomeActivity.java:1129)
1129 is the if(response.body().success == 1) { line
Let me know if you need more information on this. Thank you in advance :)
The value is Null(No data). So Null pointer exception coming. Log your Response and check data is coming or not
Your response.body() might be null and you are trying to access a field of a null object.
Try this:
if(response.isSuccessful()) {
Log.d("LOG/I", "Request sent.");
} else {
Toast.makeText(HomeActivity.this, "Request not sent.", Toast.LENGTH_SHORT).show();
}
This is because either your FCMResponse class is not mapped to the class model correctly or you are returning a null value. Make sure FCMResponse is mapped and can be deserialized correctly.
You can use #SerializedName("name_here") if you need to differ serialized field names from class field names.
EDIT
I would call the endpoint using Postman or any other similar tool and see the server's response body. I would then compare the FCMResponse class fields and field names with the response body to decide if they match exactly or not.
You FCMResponse class actually represents the body of your response not the response itself. Maybe you are mistaken there.

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