Retrofit generic response handler - android

I wish to handle all my responses in single method. the purpose is to recall the service when the response code is not 3, when the response code is 3 I intend to first refresh the token and then recall the same service.
I've created a BaseCallback class to catch one method but I can't see the log and can't catch breakpoints.
BASECALLBACK.class
public class BaseCallBack<T> implements Callback<T> {
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (!response.isSuccessful()){
Log.d("BaseCallback","Response Fail");
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
t.toString();
}
}
CALL METHOD
ApiManager.getInstance().getUsers().enqueue(new BaseCallBack<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (response.isSuccessful()){
}
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
}
});
I just want to handle my services single method.

Your starting point is good - you have an ApiManager which is the single point you're looking for - a class, NOT a method (methods shouldn't be a single contact point in this case, it will make your code unreadable and harder to debug later.
From here it would probably be better to use your own custom interface, and implement it however you wish from where you call the request, there you can handle the stuff you want, this is a very generic example that should fix some stuff and get you going.
Be mindful to the fact that this still requires you to work - tweak and add the stuff you need.
This is all you need as an interface (very basic, you can add stuff)
public interface CustomCallListener<T>
{
public void getResult(T object);
}
This is how you should use it in you ApiManager - it receives your interface as a parameter carrying the expected object type, when the response returns do what you need - parse it, cut it, whatever - and cast it into the correct object, this example uses a String response and a List return object, you can expect whatever you think and parse it accordingly, Retrofit2 allows you to parse JSON strings directly (using GSON or some other library), so it's your decision on what to use here - if you don't know what I mean - read about it.
This is also where I would add breakpoints and Log. calls to debug the response you get as you get it you can also break down rawResponse for headers and other stuff.
class ApiManager
{
// .. other stuff you need...
public void getSomeList(final CustomCallListener<List<SomeObject>> listener)
{
Call<ResponseBody> request = serviceCaller.getSomeInfo(userid);
//...other stuff you might need...
request.enqueue(new Callback<ResponseBody>()
{
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> rawResponse)
{
try
{
String response = rawResponse.body().string();
//...other stuff you might need...
//...do something with the response, convert it to
//return the correct object...
SomeObject object = new SomeObject(response);
listener.getResult(object);
}
catch (Exception e)
{
// .. the response was no good...
listener.getResult(null);
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable throwable)
{
// .. the response was no good...
listener.getResult(null);
}
});
}
}
Finally this is what you should use from anywhere in your code - this allows you to implement the callback right there and handle anything you need by what you return from the ApiManager.
ApiManager.getInstance().getSomeList(new CustomCallListener<List<SomeObject>>()
{
#Override
public void getResult(List<SomeObject> objects)
{
if (null != objects)
{
// check objects and do whatever...
}
else
{
// ... do other stuff .. handle failure codes etc.
}
}
});
Stuff to notice
As mentioned - this is a very generic skeleton that can be greatly modified (add more methods to the interface with different return types to handle Exceptions, Bad responses, other objects, add more params to the same method to handle more options, etc.) read about the subject more, beware of passing null Objects, use try and catches to avoid crashes.
Hope this Helps!

Related

What is the correct way to handle UnAuthenticated response (401) in Android retrofit

I want to take the user to the login screen whenever I encounter a 401 response from the server.
I am currently handling 401 like this:
public abstract class BaseCallback<T> implements Callback<T> {
private final Context context;
public BaseCallback(Context context) {
this.context = context;
}
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.code() == 401) {
// launch login activity using `this.context`
} else {
onSuccess(response.body());
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
}
abstract void onSuccess(T response);
}
Courtesy of https://stackoverflow.com/a/49789543/6341943
But this way, I have to pass context from my ViewModel(my ViewModel is inherited from AndroidViewModel) whenever I make an API call.
Another way I found was to use an interceptor.
I feel like there should be a better way to handle this but I couldn't find something better than this.
How does Google handle this? I couldn't find any such sample apps. I tried ioshed but couldn't find anything like this.
Please point me in the right direction.

Retrofit Call.enqueue() send requests in the order that were created?

Imagine this scenario:
I start a requestA using the Call.enqueue() method, then, before requestA be finished, I start requestB at the same endpoint of requestA. While I'm using Call.enqueue() method, requestB will be executed after requestA? Or enqueue() method is just used to do requests asynchronously?
I search that information at docs and here on StackOverflow but all the information is superficial about this specific method.
Here is my code - this same code is used for both requests:
foolRequest.enqueue(new Callback<Response>() {
#Override
public void onResponse(#NonNull Call<Response> call,
#NonNull retrofit2.Response<Response> response) {
//do something
}
#Override
public void onFailure(#NonNull Call<Response> call,
#NonNull Throwable t) {
//do something
}
});
I think so,
Otherwise, if you implement your own connection client.
By the source code from OkHttpClient, there is a dispatcher class, save all the enqueue API, and it uses queue to save the relative task
synchronized void enqueue(AsyncCall call) {
if (runningAsyncCalls.size() < maxRequests && runningCallsForHost(call) < maxRequestsPerHost) {
runningAsyncCalls.add(call);
executorService().execute(call);
} else {
readyAsyncCalls.add(call);
}
}

Implementing Retry button for Retrofit error on LiveData observable

learner here and I'm trying to add a retry button for whenever there is an error in Retrofit Callback#onFailure method.
Somewhat following the Android Architecture Guide, I'm able to call, persist the data and show it on RecyclerView. Here is a general flow of what I've done so far:
On PagedList.BoundaryCallback I'm getting the response and saving it. Here I've also created a LiveData of NetworkState, which I'm observing within MainActivity through ViewModel class.
#Override
public void onZeroItemsLoaded() {
if (isFetchNeeded())
mClient.fetchFirstNetworkCall().enqueue(getRetrofitCallback());
}
#Override
public void onItemAtEndLoaded(#NonNull Item itemAtEnd) {
if (mNextPageToken != null)
mClient.fetchNextNetworkCall(mNextPageToken).enqueue(getRetrofitCallback());
}
#Override
public void onResponse(#NonNull Call<BloggerApi> call, #NonNull Response<BloggerApi> response) {
mObservableNetwork.setValue(NetworkState.LOADING);
if (response.isSuccessful()) {
mExecutors.diskIO().execute(() -> {
insertItemsToDb(responseBody.getItems());
mObservableNetwork.postValue(NetworkState.SUCCESS);
});
} else {
String error = response.errorBody() == null ? "Unknown Error" : String.valueOf(response.errorBody());
mObservableNetwork.setValue(NetworkState.error(error));
}
}
#Override
public void onFailure(#NonNull Call<BloggerApi> call, #NonNull Throwable t) {
mObservableNetwork.setValue(NetworkState.error(t.getMessage()));
}
And then on UI:
mViewModel.getNetworkState().observe(this, networkState -> {
if (networkState.getStatus() == Status.ERROR) {
retryButton.setOnClickListener(view -> {
// todo: Implement what to do
});
}
});
I'm lost here and don't know how implement a Retry button to make the last call if for some reason I get an error. Can you please help me out about what the Retry button should actually do to get the callback?
Thank you.
P.S. I'm new to Java, and as of now Kotlin is out of my league so couldn't figured out how Google sample projects implementing the retry method, and, also my sample project is on GitHub/DemoBlogApp for any reference. Any help is appreciated.
Figured it out myself long time back but was waiting for an opinion. While I didn't got any, thought of posting an answer to myself so that others may find it useful.
Retrofit has clone() method which can be super useful for situations for failures. So basically, make an interface:
public interface RetryCallback<T> {
void getCall(Call<T> call, ApiCallback<T> callback);
}
On Retrofit failure:
class Repository {
RetryCallback<Api> retryCallback;
//...
new Callback<Api>() {
// other Callback methods
public void onFailure(Call<Api> call, Throwable t) {
retryCallback.getCall(call, this);
}
}
public void setRetryCallback(RetryCallback<Api> retryCallback) {
this.retryCallback = retryCallback;
}
}
On MainActivity:
//...
// Using lambda instead of initializing with new operator
viewModel.setRetryCallback((call, callback) ->
call.clone().enqueue(callback);
);

How to return the value from retrofit onResponse?

I'm a beginner with Android and have written a simple retrofit example:
LOGININTERFACE mAPISERVICE;
mAPISERVICE= LOGINGENERATOR.getAPIServices();
mAPISERVICE.savePost("0016642902","0016642902","password").enqueue(new Callback<LGOINMODEL>() {
#Override
public void onResponse(Call<LGOINMODEL> call, Response<LGOINMODEL> response) {
LGOINMODEL output=response.body();
if(response.isSuccessful())
test[0] ="behzad behzad behzad";
}
#Override
public void onFailure(Call<LGOINMODEL> call, Throwable t) {
}
});
But in this line:
test[0] ="behzad behzad behzad";
It can't return a value, and have not read any tutorials, examples, or other posts on Stack Overflow, and so still cannot solve this problem.
you need a callback so whenever you make a call to this function to get the values from the server you can pass the callback and when results are there it can give you back.
Callback interface
MyResultsListener.java
public interface MyResultsListener{
public void onData(LGOINMODEL loginModel);
public void onFailed();
}
now suppose the call you made is in some function then pass the MyResultsListener reference as a parameter to get back the results.
public void makeServerCall(MyResultsListener listener){
LOGININTERFACE mAPISERVICE;
mAPISERVICE= LOGINGENERATOR.getAPIServices();
mAPISERVICE.savePost("0016642902","0016642902","password").enqueue(new Callback<LGOINMODEL>() {
#Override
public void onResponse(Call<LGOINMODEL> call, Response<LGOINMODEL> response) {
LGOINMODEL output=response.body();
if(response.isSuccessful())
test[0] ="behzad behzad behzad";
//for getting back data to calling class or function
listener.onData(output);
}
#Override
public void onFailure(Call<LGOINMODEL> call, Throwable t) {
//for failure handling
listener.onFailed();
}
});
}
With retrofit2 is possible make synchronous call:
mAPISERVICE = LOGINGENERATOR.getAPIServices();
Callback<LOGINMODEL> mLOGINMODEL = mAPISERVICE.savePost("0016642902","0016642902","password");
Response<LOGINMODEL> response = mLOGINMODEL.execute();
...
however, synchronous requests trigger app crashes on Android 4.0 or newer. You’ll run into the NetworkOnMainThreadException error.
More information here.

How To Intercept a response that requires user action before replaying desired response to user

I have a rather unique problem I have to solve. I am working on an app that uses a homegrown networking layer with the legacy okhttp client in Android and we would like to use Retrofit and OKHttp3 instead.
The problem we have is that our services may return a response on a user request that contains one or more actions a user must take before the server will allow the original request to execute and thus return the desired response.
It goes kinda like this. User tries to log in, but they are a new user, instead services responds with a series of information collection actions. If the user cancels they do not log in, but if the user executes the series of actions than the original login request is replayed and the user logs in.
I am taking a close look at the Interceptor interface provided by OkHttp3 as the means of achieving this. Now I know I can intercept any response and modify it or do other things, but the interface method must return a value of type Response in the end. However I need the ability to "hold/prevent" that return from taking place before the user either A: Cancels the actions required by services, or B: Successfully completes those actions.
This is what I have so far.
private static class ServerActionInterceptor implements Interceptor {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
Gson gson = new Gson();
ServicesResponse serviceResponse = null;
if (response.body() != null) {
serviceResponse = gson.fromJson(response.body().string(), ServicesResponse.class);
}
if (serviceResponse != null) {
if (serviceResponse.getServerActions() != null && serviceResponse.getServerActions().size() > 0) {
//handle server actions
handleServerActions(serviceResponse);//this must complete before we can return chain.proceed IF there are actions the user must take.
}
}
return chain.proceed(chain.request());
}
//this method may end up returning a value
private void handleServerActions(ServicesResponse serviceResponse) {
//process server actions
}
}
Its not a whole lot, the majority will happen in handleServerActions and that honestly is pretty particular to the app. But the core of the issue, is what method should I employ so that the return will happen if there are no serverActions for the user request OR there are and the user has completed the actions and thus the true response is ready to be delivered to the client?
Thanks.
Due to a comment, I thought I would share my solution.
It involved a custom abstract class that implements Callback<T>.
This is just a general example and the actual implementation would be up to the business rules for your project.
public abstract class CustomCallback<T extends CustomResponse> implements Callback<T> {
private CustomExcetption encounteredException;
private Class<T> clazz;
public CustomCallback(Class<T> clazz) {
this.clazz = clazz;
}
#Override
public void onResponse(#NonNull Call<T> call, #NonNull Response<T> response) {
if (response.body() != null) {
T body = response.body();
i
if (body != null && body.hasCustomActions() != null && body.hasCustomActions().size() > 0) {
handleCustomAction(body);
} else {
onSuccessfulResponse(body);
}
} else if (response.errorBody() != null) {
onFailureResponse(exception);
}
}
#Override
public void onFailure(#NonNull Call<T> call, #NonNull Throwable throwable) {
onNetworkFailure(call, exception);
}
public abstract void onSuccessfulResponse(#Nullable T response);
public abstract void onFailureResponse(CustomException exception);
public abstract void onNetworkFailure(Call<T> call, CustomException exception);
public void handleCustomAction(#NonNull final T body, final String path) {
//handle custom actions.
if(customActionsHandledSuccessfully) {
onSuccessfulResponse(body);
} else {
//create your exception somehow
CustomException createdException = new CustomException();
onFailureResponse(createdException);
}
}
}
Ussage example:
Keep in ming the reference to SomeResponse.class is something I needed for my implementation and may not be needed in others. It was used for gson serialization purposes as I needed to know the concrete class of the response to serialize it properly.
service.someApi().enqueue(new CustomCallback<SomeResponse>(SomeResponse.class) {
#Override
public void onSuccessfulResponse(#Nullable SomeResponse response) {
}
#Override
public void onFailureResponse(CustomException exception) {
}
#Override
public void onNetworkFailure(Call<SomeResponse> call, CustomException exception) {
}
});

Categories

Resources