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);
}
}
Related
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);
);
Suppose we have a retrofit Callback as follows:
Callback<T> callback = new Callback<T>() {
#Override
public void onResponse(Call<T> call, Response<T> response) {
mProgressBar.dismiss();
if (response.code() == 401) {
Toast.makeText(getApplicationContext(),
"Invalid session, logging out",
Toast.LENGTH_LONG).show();
Session.getInstance(getApplicationContext()).clear();
this.navigateToLoginActivity();
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
}
}
Suppose the retrofit request with this callback runs for a long time and if the activity starting the request has already finished by the time onResponse is invoked, then it will cause memory leak we are referring to the activity via
this.navigateToLoginActivity()
So the question is how to get the context in onResponse? WeakReference? But this is such a common use case, i.e. to logout user, in case, the authentication fails. I want to know the best practice in this scenario for a general AsyncTask case and retrofit case as well.
Let's say I have a User view model to load the user's data. I would use Retrofit2 to load the users data. A simple example would be like this:
call.enqueue(new Callback<User>() {
#Override
public void onResponse(Call<User> call, Response<User> response) {
if (response.isSuccessful()) {
userMutableLiveData.setValue(response.body());
} else {
//since response.body() is null, how to set a suitable value to userMutableLiveData.setValue()
}
}
#Override
public void onFailure(Call<User> call, Throwable t) {
//since there was a networking failure, how to set a suitable value to userMutableLiveData.setValue()
}
});
Now my question is when retrofit returns response code is not successful or a networking failure occur how do I set a suitable value so that I can give the user a good feedback of what happened.
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.
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!