I had a retrofit request, when I get data in onResponse,
I did multiples insert in textviews which I called heavy work in the code above, I get the result from OnReponse if there's one, else I get result from database, so the problem I had the same code in OnResponse and OnFailure, so there's any way to put my heavy work outside retrofit, and wait the response to get just one result from OnResponse or OnFailure ??
call.enqueue(new Callback<Dashboard>() {
#Override
public void onResponse(Call<Dashboard> call, Response<Dashboard> response) {
realm.beginTransaction();
dashboard = realm.copyToRealmOrUpdate(response.body());
realm.commitTransaction();
// heavy work : insert in data in multiple text views
}
#Override
public void onFailure(Call<Dashboard> call, Throwable t) {
Log.e("error ", "" + t.getMessage());
dashboard = realm.where(Dashboard.class).findFirst();
// heavy work : insert in data in multiple text views
}
}
Try this..
First Create an interface ..Let's call it OKCallback.
public interface OKCallback {
void onSuccess(String result);
void onFailure(String result);
}
Then in your method that launches the retrofit request, pass final OKCallback okCallback like this..
public void NetworkCall(final OKCallback okCallback){
...........
call.enqueue(new Callback<Dashboard>() {
#Override
public void onResponse(Call<Dashboard> call, Response<Dashboard> response) {
realm.beginTransaction();
dashboard = realm.copyToRealmOrUpdate(response.body());
realm.commitTransaction();
// heavy work : insert in data in multiple text views
okCallback.onSuccess(parcel);
}
Finally simply (ActivityX implements OKCallback) in any class or activity and you should be able to do your heavy work there..You can also wrap your data in the onSuccess methods with a Handler as shown.
#Override
public void onSuccess(String result) {
Handler handler = new Handler(ActivityX.this.getMainLooper());
//process result and
handler.post(new Runnable() {
#Override
public void run() {
//heavy work done here will run on UI thread
}
});
}
You can make an interface and get call back on main thread or after getting response of api call in onSuccess() or in onfailure() start a new AsynTask and process the request in background.
You can change it like this
//create a interface
public interface ConfirmationCallback {
void onSuccess(YourResponseClass value);
void onError();
}
//call this method from your class
yourApiCall(new ConfirmationCallback() {
#Override
public void onSuccess(YourResponseClass value) {
realm.beginTransaction();
dashboard = realm.copyToRealmOrUpdate(value);
realm.commitTransaction();
// heavy work : insert in data in multiple text views
}
#Override
public void onError() {
dashboard = realm.where(Dashboard.class).findFirst();
// heavy work : insert in data in multiple text views
}
});
public void yourApiCall(final ConfirmationCallback confirmationCallback){
call.enqueue(new Callback<Dashboard>() {
#Override
public void onResponse(Call<Dashboard> call, Response<Dashboard> response) {
confirmationCallback.onSuccess(response.body());
}
#Override
public void onFailure(Call<Dashboard> call, Throwable t) {
Log.e("error ", "" + t.getMessage());
confirmationCallback.onError();
}
}
}
Related
I would like to use PublishProcessor that would work like that:
is fed with some init data (randomly when user scrolls view - Integer in the example),
it does some background job (downloads data based on the init data)
when finish downloading notifies about newly downloaded data type (String in the example)
It should be all the time ready to receive and handle new init data.
I've prepared simple code to test it but it doesn't work.
PublishProcessor processor = PublishProcessor.create();
processor.map(new Function<Integer, String>() {
#Override
public String apply(Integer o) throws Exception {
Thread.sleep(1000);
DevLog.d("test","executing "+o);
return String.valueOf(o)+"aaa";
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<String>() {
Subscription sub;
#Override
public void onSubscribe(org.reactivestreams.Subscription s) {
DevLog.d("test","onsubscribe "+s);
sub = s;
sub.request(1);
}
#Override
public void onNext(String s) {
DevLog.d("test","next "+s);
sub.request(1);
}
#Override
public void onError(Throwable t) {
DevLog.d("test","error "+t);
}
#Override
public void onComplete() {
DevLog.d("test","complete");
}
});
processor.onNext(666);
processor.onNext(123);
processor.onNext(456);
DevLog.d("test","nextsent");
All I get in the logcat is:
onsubscribe 0
nextsent
I would rather expect:
nextsent
executing 666
executing 123
executing 456
next 666aaa
next 123aaa
next 456aaa
I'm here with a new question about Android and Retrofit. I was wondering what is the correct way to handle multiple async calls on a Android Activity and each of this one, onResponse returns a value used by the next call, because if I understood fine, the calls runs on background which means that if the call didn't finish you returned value will be null until you get a successful response.
I was thinking to achieve that with something like this(only the basis):
private List<SomeModel> mylist;
final Call<List<SomeModel>> call1 = client.getSomeValues1();
final Call<List<SomeModel2>> call2 = client.getSomeValues2();
call1.enqueue(new Callback<SomeModel>() {
#Override
public void onResponse(SomeModel> call, Response<SomeModel> response) {
if (response.isSuccessful()) {
// Set mylist to response.body()
mylist = response.body();
}
}
#Override
public void onFailure(Call<SomeModel> call, Throwable t) {
mylist = null;
}
});
call2.enqueue(new Callback<SomeModel2>() {
#Override
public void onResponse(SomeModel2> call, Response<SomeModel2> response) {
if (response.isSuccessful()) {
// Do something with my list and also with call2 response
if(mylist != null) {
for (SomeModel singleObject: mylist) {
// Do something with each object
}
}
}
}
#Override
public void onFailure(Call<SomeModel2> call, Throwable t) {
// Do something with fail call
}
});
With something like the example before because the calls are running on background and maybe the call2 finish first then mylist value will be null because call1 hasn't finished yet.
Also I was thinking to put call2 inside call1 onResponse but I don't feel that right. I have to say that I'm still learning, I'm pretty rookie.
So, What is the correct why to handle this and how? Thanks. I hope my question is understandable.
Thanks for your suggestion #insa_c
I achieved that with RXJava2, here is how I did it explained general way:
First you need to define your api services as Observables
#GET("url-1/")
Observable<List<Model1>> service1();
#GET("url-2/")
Observable<List<Model2>> service2();
Now in your activity make the calls this way:
final Observable<List<Model1>> call1 = APIClient.getClient().create(APIService.class).service1();
final Observable<List<Model2>> call2 = APIClient.getClient().create(APIService.class).service2();
Now lets make an unique observable with zip (I used a List because I'm adding two different kind of objects to the list that I'll get when both calls finish):
<Observable<List<Object>> test = Observable.zip(call1, call2, new BiFunction<List<Model1>, List<Model2>, List<Object>>() {
#Override
public List<Object> apply(List<Model1> objects1, List<Model2> objects2) throws Exception {
List<Object> list = new ArrayList<>();
list.addAll(objects1);
list.addAll(objects2);
return list;
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread());
Now the only thing left to do is to subscribe and decide what you want to do with the content of both responses:
test.subscribe(new Observer<List<?>>() {
#Override
public void onSubscribe(Disposable d) {
Log.d("TAG", "onSubscribe");
}
#Override
public void onNext(List<?> objects) {
// You can split the list by object in here with an instanceof to determine if it's Model1 or Model2
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
// Do something here, both calls finished, if you stored something in onNext method you can use it here.
}
});
I hope this info will be helpful for someone.
I have a fragment with a nested retrofit call inside an on response callback. I am cancelling both requests on the onStop method of the fragment, which according to the logs and debugging it's being call thus the cancel() for both retrofit calls are being called too.
Here is the code for the nested calls
serviceRequestTypesResponseCall.enqueue(new RetrofitCallback<ServiceRequestTypesResponse>(retrofit) {
#Override
public void onResponse(RetrofitResult<ServiceRequestTypesResponse> result) {
// get url from response and enqueue nested call
detailedServiceRequestTypeCall = scfServiceV2.getRequestType(result.getRequestTypeUrl());
detailedServiceRequestTypeCall.enqueue(new RetrofitCallback<DetailedServiceRequestType>(retrofit) {
#Override
public void onResponse(RetrofitResult<DetailedServiceRequestType> result) {
// this is being reached when cancelled
}
#Override
public void onFailure(RetrofitResult<DetailedServiceRequestType> error) {
// do something
}
});
}
#Override
public void onFailure(RetrofitResult<ServiceRequestTypesResponse> error) {
// do something
}
});
public void onStop() {
super.onStop();
// nested call
if(detailedServiceRequestTypeCall != null) {
detailedServiceRequestTypeCall.cancel();
}
// outer call
if(serviceRequestTypesResponseCall != null) {
serviceRequestTypesResponseCall.cancel();
}
}
The nested call gets a dynamic url from the outer call response, thus this is how it's defined in it's retrofit interface
#GET
Call<DetailedServiceRequestType> getRequestType(#Url String url);
also, this is how I am handling Retrofit cancellation in my custom callback class
public abstract class RetrofitCallback<T> implements Callback<T> {
private static final String TAG = "RetrofitCallback";
protected final Retrofit retrofit;
public RetrofitCallback(Retrofit retrofit) {
this.retrofit = retrofit;
}
public abstract void onResponse(RetrofitResult<T> result);
public abstract void onFailure(RetrofitResult<T> error);
#Override
public final void onResponse(Call<T> call, Response<T> response) {
onResponse(new RetrofitResult<>(retrofit, response));
}
#Override
public final void onFailure(Call<T> call, Throwable t) {
if (call.isCanceled()) {
Log.d(TAG, "Cancelled => " + call.request().toString());
} else {
onFailure(new RetrofitResult<T>(t));
}
}
}
The fragment is inside a bottombar navigation (https://github.com/roughike/BottomBarbottombar) which basically replaces the fragments upon tab selection. The requests are happening when the fragment gets displayed so switching the fragments quickly so to not give time for the nested call to finish reproduces this issue.
The logcat only shows the cancel log for the outer call. Not sure why I am not getting the same for the nested call despite being explicitly cancelled . This causes the request to finish and try to do some ui related logic and reference the activity which is no longer available because the fragment has been replaced when switching fragments
I have a fragment set up like so:
public mFragment extends Fragment implements Callback<mType> {
...
#Override
public void onViewCreated(View v, Bundle sis) {
Retrofit retrofit = new Retrofit.Builder().baseUrl("MYURL").addConverterFactory(GsonConverterFactory.create()).build();
api mAPI = retrofit.create(api.class);
Call<mType> call1 = mAPI.query1("query1"));
Call<mType> call2 = mAPI.query2("query2"));
call1.enqueue(this);
call2.enqueue(this);
}
#Override
public void onFailure(Throwable t) {
...
}
#Override
public void onResponse(final Response<mType> response, Retrofit retrofit) {
...
}
}
I need to make 2 api calls, which both return the same type. However, I want to handle both of them in different onResponse methods as I need to do distinct things to both of them. This is under Retrofit 2.0.
This is an API of a different service, so I do not have access to change any of the responses.
Is there a way to specify which method a Retrofit Call calls back to? I'm really hoping that this has as clean of a solution as if I were using two different return types. If worst comes to worst I can just duplicate the object and rename it but I think there is a "correct" way to do this.
queue your requests separately. So your response listeners will be separate for both the requests
call1.enqueue(new Callback<String>() {
#Override
public void onResponse(Response<String> response) {
}
#Override
public void onFailure(Throwable t) {
}
});
and
call2.enqueue(new Callback<String>() {
#Override
public void onResponse(Response<String> response) {
}
#Override
public void onFailure(Throwable t) {
}
});
I have to use a legacy library that using AsyncTask for a background job. How I can wrap an AsyncTask by an Observable object which I'm using on my current project.
The AsyncTask is encapsulated so I cannot access the synchronous call inside AsyncTask.
say you have an object asynchronousCall executing some async work with call() method which takes callback as a param, you can wrap it like that :
Observable.create(new Observable.OnSubscribe<Object>() {
#Override
public void call(final Subscriber<? super Object> subscriber) {
asynchronousCall.call(new CallBack() {
#Override
public void success(Object o) {
subscriber.onNext(o);
subscriber.onCompleted();
}
#Override
public void error(Throwable t) {
subscriber.onError(t);
}
});
}
});
Another approach to the case when you cannot or don't want to wrap execution in an Observable is to use Subjects:
public static void main(String[] args) {
Subject<Object, Object> subject = PublishSubject.create();
Listener listener = new Listener() {
#Override
public void onCallback(Object object) {
subject.onNext(object);
subject.onCompleted();
}
};
subject.subscribe(object -> yourReaction(object));
someMethodWithCallback(listener);
}
public interface Listener {
void onCallback(Object object);
}
Subject being an Observer allows you to send items into it, while it being an Observable allows you to subscribe to it and receive these events.