Retrofit Custom callback and unchecked assignments - java

I am trying to create my own retrofit callback handler because I want to hide a loading screen once the call is done and would rather not repeat the call. I'm getting a Unchecked Assignment warning when using my custom RetrofitCallback and not retrofit2.Callback. What is the correct way to fix this warning?
public abstract class RetrofitCallback<T> implements Callback {
private BaseActivity mContext;
public RetrofitCallback(Context context) {
mContext = (BaseActivity) context;
}
#Override
public void onResponse(Call call, Response response) {
mContext.hideLoading();
onSuccess(response);
}
#Override
public void onFailure(Call call, Throwable t) {
mContext.hideLoading();
}
public abstract void onSuccess(#NonNull Response<T> response);
}
Call being made
service.getSignupCode(request).enqueue(new RetrofitCallback<SignupResponse>(this) {
#Override
public void onSuccess(#NonNull Response<SignupResponse> response) {
}
});

Can you try this below code?. The one mistake I can figure out is implementing Callback instead of Callback<T>
public abstract class RetrofitCallback<T> implements Callback<T> {
private BaseActivity mContext;
public RetrofitCallback(Context context) {
mContext = (BaseActivity) context;
}
#Override
public void onResponse(Call call, Response response) {
mContext.hideLoading();
onSuccess(response);
}
#Override
public void onFailure(Call call, Throwable t) {
mContext.hideLoading();
}
public abstract void onSuccess(#NonNull Response<T> response);
}

Related

how to hook function with xposed?

for example,i decompile the app and want to hook function with xposed,how can i do that?
package com.my.app;
public class myClass
{
public static int myFunction()
{
//do something inside
}
}
how to hook "myFunction" with xposed?
Have you tried to simply adapt the available examples to your class and method?
XposedHelpers.findAndHookMethod("com.my.app.myClass", lpparam.classLoader, "myFunction", new XC_MethodHook() {
#Override
protected void beforeHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("before myFunction()");
}
#Override
protected void afterHookedMethod(MethodHookParam param) throws Throwable {
XposedBridge.log("after myFunction()");
}
);

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.

How to properly override Runnable.run(), so it can take parameters?

I am currently working on an android project and came across the situation, that I have to pass a function as parameter, so I browsed StackOverflow and tried out solution, to encapsulate the function with a Runnable.
The problem I'm now facing is, that the function I want to pass, expects parameters and Runnable.run() doesn't take any. So I'm currently trying to extend Runnable and override run() to let it accept parameters, which I can then pass to the actual function.
Now I'm kinda unsure, as run() is used in Thread on how I can override it, without knocking out Thread. I don't use any other Thread methods so far. Any suggested approach?
EDIT
The target is, to call these custom Runnables inside a listener method like so:
public void startRequest(RequestOperation operation, final Runnable onSuccess, final Runnable onError, final Runnable onFinished) {
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, instance_context.getResources().getString(R.string.base_url), null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
onSuccess.run();
onFinished.run();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
onError.run();
onFinished.run();
}
});
queue.add(request);
}
In best case, onSuccess is able get the response object passed, as onError gets the error.
Why force Runnable into being something it's not designed to be?
Simply have your own interface that has the signature you require.
E.g.
public interface Callback<T> {
void run(T parameter);
}
public void startRequest(RequestOperation operation, final Callback<JSONObject> onSuccess, final Callback<VolleyError> onError, final Runnable onFinished) {
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, instance_context.getResources().getString(R.string.base_url), null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
onSuccess.run(response);
onFinished.run();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
onError.run(error);
onFinished.run();
}
});
queue.add(request);
}
Or even better (if you ask me):
public interface Callback {
void onSucces(JSONObject response);
void onError(VolleyError error);
void onFinished();
}
public void startRequest(RequestOperation operation, final Callback callback) {
JsonObjectRequest request = new JsonObjectRequest(Request.Method.POST, instance_context.getResources().getString(R.string.base_url), null, new Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
callback.onSuccess(response);
callback.onFinished();
}
}, new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
callback.onError(error);
callback.onFinished();
}
});
queue.add(request);
}
You can also make Callback generic again, if you need to:
public interface Callback<R, E> {
void onSucces(R response);
void onError(E error);
void onFinished();
}
public void startRequest(RequestOperation operation, final Callback<JSONObject, VolleyError> callback) {...}
To use:
public class SimpleCallback implements Callback {
public void onSucces(JSONObject response) {
doSomethingWithResponse(response);
}
public void onError(VolleyError error) {
doSomethingWithError(error);
}
void onFinished() {
logFinishTime();
}
}
startRequest(operation, new SimpleCallback());

How can I change a class to an interface in this condition?

Here is my definition:
public abstract class APICallback<T extends CommonModel.APIDataModel> implements Callback<CommonModel<T>>
I found that I can not convert APICallback back to Callback.
What is the problem?
How can I do it correctly?
Thank you!
----Update----
In fact I am using retrofit, I defined APICallback implements retrofit.Callback
Here is the exact code :
public abstract class APICallback<T extends CommonModel.APIDataModel> implements Callback<CommonModel<T>> {
private Context context;
public APICallback(Context context) {
this.context = context;
}
public abstract void onResponse(T response);
#Override
public void onResponse(Response<CommonModel<T>> response, Retrofit retrofit) {
T data = response.body().data;
if (response.body().isSuccess()) {
onResponse(data);
onEnd();
} else {
dispatchError(response.body());
}
}
#Override
public void onFailure(Throwable t) {
LogUtils.d("Network error or exception", t.getMessage());
ViewUtils.showMessage(t.getMessage());
onEnd();
}
public void dispatchError(CommonModel<T> response) {
LogUtils.d("API error", response.toString());
ViewUtils.showMessage(response.data.msg);
onEnd();
}
public void onEnd() {
if (context instanceof BaseActivity) {
((BaseActivity) context).getLoading().hide();
}
}
I am using it like this:
getClient().login(u, p).enqueue(new APICallback<UserModel>(this) {
#Override
public void onResponse(UserModel response) {
Intent intent = new Intent(LoginActivity.this, MainActivity.class);
startActivity(intent);
finish();
}
});
enqueue :
void enqueue(Callback<T> callback);
login :
Call<UserModel> login(#Field("user_name") String userName, #Field("user_password") String userPassword);
The compiler tell me that can not convert anonymous APICallback<UserModel> to Callback<UserModel>
I am confused.
I didn't notice that my login method definition doesn't match Callback<CommonModel<T>>.
After I change it to
Call<CommonModel<UserModel>> login(#Field("user_name") String userName, #Field("user_password") String userPassword);
everything is ok.

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