Retrofit 2 + RxJava cancel/unsubscribe - android

I am performing a network request where I send files and a message.
I would like to have an option to cancel current request. I have found two similar questions and both suggests that observable.subscribe(Observer) returns Subscription object which has method unsubscribe().
Here is the first one
And the second one
In my case, I use observable.subscribe(Observer) which is void. Here is my code:
Observable<MessengerRaw> observable = mModel.sendMessage(message, companion, description, multiParts);
observable.subscribe(new Observer<MessengerRaw>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(MessengerRaw value) {
if (getView() != null) {
((MessengerActivity) getView()).resetMessegeView();
((MessengerActivity) getView()).updateMessageList();
}
}
#Override
public void onError(Throwable e) {
getData().remove(0);
if (getView() != null) {
((MessengerActivity) getView()).updateMessageList();
}
}
#Override
public void onComplete() {
hideProgress();
}
});
So how do I unsubscribe/cancel my request?
Thank you.

In RxJava2, you can get Disposable object in onSubscribe callback method of oserver, which you can use to dispose subscription.

In RXJava You must use subscriptions for unsubscribe
private Subscription mSubscription;
/.../
Observable<MessengerRaw> observable = mModel.sendMessage(message, companion, description, multiParts);
Subscription subscription = observable.subscribe(new Observer<MessengerRaw>() {/.../});
When you want to unsubscribe you can call
if(!subscription.isUnsubscribed()){
subscription.unsubscribe();
}
In RXJava 2 observable.subscribe(new Observer<MessengerRaw>() {/.../}); returns Disposable object, you can call dispose();

Related

how to fetch observables in parallel, wherein only one api call has a retry logic

I want to implement a logic using RxJava in my android application, which requires three parallel api calls. Only the third api call has a retry logic. If, after having three attempts, the success is achieved then a subsequent call will be made for the fourth api, else only the result of first and second api calls will be passed on to the subscriber.
I tried to achieve this using Zip operator but then got stuck with retry logic for third api call.
Observable<String> observable1 = Observable.just("A","B");
Observable<Integer> observable2 = Observable.just(1,2);
Observable<Boolean> observable3 = Observable.just(Boolean.TRUE, Boolean.FALSE);
Observable.zip(observable1, observable2, observable3, new Function3() {
#Override
public Object apply(String s, Integer integer, Boolean aBoolean) throws Exception {
if (aBoolean==null){
alphabets3.retry(3).doOnComplete(new Action() {
#Override
public void run() throws Exception {
// the result will never be used
}
});
}
return s+integer+aBoolean;
}
}).subscribe(new Observer<Object>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Object o) {
Log.e("onNext-->", o.toString());
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
if any Observable failed in the Zip operator, Zip will fail the stream, the only way I know to achieve parallel execution and error handling with Zip, is to add onErrorResumeNext to each Observable, that map the error to a new model to deal with later .. and handling what you want to do in the zip mapping function ... for example
Obsevable.zip(
observable1.onErrorResumeNext{Observable.just(Model(it)},
observable2.onErrorResumeNext{Observable.just(Model(it)},
observable3.retryWhen {t is TimeOutException} //here you can add your retry logic
.onErrorResumeNext(t -> Observable.just(Model(t)),(m1 , m2, m3) -> Result())

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);
);

multiple api request using retrofit and rx java

I am new to android and I have a scenario where I want to get get data from multiple api. Let suppose api_a, api_b, api_c, api_d. These api are independent of each other but I want to show data from these api in a mix Recycler View (horizontal and vertical). So I want to make these api call in such a manner so that I can get every api data at a time so that i can display in recycler view.
I already using retrofit 2 but for that I had to chain them one by one which is very lengthy and I think this is not a feasible approach. I know little bit about RX JAVA ,but I only know how to make one request at a time. Please help
There are at least 2 ways to achieve this -
1) Using RxJava Zip operator (for parallel requests)
Get all the observables
Observable<ResponseType1> observable1 = retrofit.getApi_a();
Observable<ResponseType2> observable2 = retrofit.getApi_b();
Observable<ResponseType3> observable3 = retrofit.getApi_c();
Zip the observables to get a final observable
Observable<List<String>> result =
Observable.zip(observable1.subscribeOn(Schedulers.io()), observable2.subscribeOn(Schedulers
.io()), observable3.subscribeOn(Schedulers.io()), new Function3<ResponseType1, ResponseType2, ResponseType3, List<String>>() {
#Override
public List<String> apply(ResponseType1 type1, ResponseType2 type2, ResponseType3 type3) {
List<String> list = new ArrayList();
list.add(type1.data);
list.add(type2.data);
list.add(type3.data);
return list;
}
});
now subscribe on the resultant observable
result.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new Observer<List<String>>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(List<String> s) {
Log.d(TAG, "s is the list with all the data");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage());
}
#Override
public void onComplete() {
}
});
2) Using RxJava flatMap() operator. (To request serially one after another)
This is simple chaining of requests
List<String> result = new ArrayList<>();
Disposable disposable = retrofit.getApi_a()
.subscribeOn(Schedulers.io())
.flatMap((Function<ResponseType1, ObservableSource<ResponseType2>>) response1 -> {
result.add(response1.data);
return retrofit.getApi_b();
})
.flatMap((Function<ResponseType2, ObservableSource<ResponseType3>>) response2 -> {
result.add(response2.data);
return retrofit.getApi_c();
})
.map(response3 -> {
result.add(response3.data);
return response3;
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<Response3>() {
#Override
public void onNext(Response3 response3) {
Log.d(TAG, "result variable will have all the data");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, e.getMessage());
}
#Override
public void onComplete() {
}
});
For combining multiple Observables you may want to consider the Merge operator.
This would allow you to combine the stream of multiple requests into a single Observable.
Merge will interleave them as they are emitted. If sequence matters, there is also Concat which will emit from each Observable before continuing with the next.
Rx Doc
Merge: http://reactivex.io/documentation/operators/merge.html
Concat: http://reactivex.io/documentation/operators/concat.html
Merge operator combines multiple observable into one
Set up Base URL of API:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(oktHttpClient.build())
.build();
Now setup two observables for the two network requests:
Observable<JsonElement> Observable1 = ApiClient.getApiService().getApi_1();
Observable<JsonElement> Observable2 = ApiClient.getApiService().getApi_2();
Now we use RxJava's mergemethod to combine our two Observables:
Observable.merge(Observable1, Observable2 )
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<JsonElement>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(JsonElement value) {
Log.d("RESPONSE", "onNext:=======" + value);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.d("RESPONSE", "DONE==========");
}
});

Using RxJava with Paho MQTT

In my Android app, I have a service which has an instance of a class(call it MQTTClient) which publishes or subscribes to an MQTT server. I want to use RxJava with Eclipse Paho Android to manage MQTT subscribe and publish operations.
I am using Single observable and SingleObserver for publishing, and Flowable observable and Observer for subscribing. But I am stuck at a point where I cannot figure out when and how to dispose of the Disposable.
Here is the Single Observable from the publish method in MQTTClient
Single<IMqttToken> pubTokenSingle = Single.create(new SingleOnSubscribe<IMqttToken>() {
#Override
public void subscribe(final SingleEmitter<IMqttToken> emitter) throws Exception {
final IMqttToken token = client.publish(topic, mqttMessage);
token.setActionCallback(new IMqttActionListener() {
#Override
public void onSuccess(IMqttToken asyncActionToken) {
emitter.onSuccess(token);
}
#Override
public void onFailure(IMqttToken asyncActionToken, Throwable exception) {
boolean hasNetwork = isOnline(context);
if (hasNetwork && Objects.equals(((MqttException) exception).getReasonCode(),
MqttException.REASON_CODE_CLIENT_NOT_CONNECTED)) {
//connect client and retry MQTT pub
try {
//connect() is a method in MQTTClient
//connect() method also utilizes RxJava2 Single.
//Same issue of disposing a `Disposable` exists in that method as well
connect();
//call the publish method again
} catch (MqttException e) {
e.printStackTrace();
emitter.onError(e);
}
} else if (!hasNetwork) {
emitter.onError(exception);
} else {
emitter.onError(exception);
}
}
});
}
});
Here is the SingleObserver
final Disposable[] disposable = new Disposable[1];
SingleObserver<IMqttToken> pubTokenSingleObserver = new SingleObserver<IMqttToken>() {
#Override
public void onSubscribe(Disposable d) {
disposable[0] = d;
}
#Override
public void onSuccess(IMqttToken iMqttToken) {
//disposable[0].dispose();
//Planning to use the above as last resort
//Also thought of moving this to doOnSuccess
}
#Override
public void onError(Throwable e) {
//Put topic name, and mqtt message in SQLite
//disposable[0].dispose();
//Planning to use the above as last resort
//Also thought of moving this to doOnError
}
};
Someone suggested that I have a cleanup method in the concerned class which gets called when onStop is invoked.
I am concerned what would happen in case I use disposable.dispose() and the network operation is still in progress.
How do I ensure that if the operation is incomplete then at least the details persist in the SQLite DB?
I am hoping that the solution would be easily extensible for subscribing as well. If not then tell me about the possible pitfalls.
This is a learning project where I am learning RxJava2 that is why I didn't opt for RxMQTT.

RxJava flatmap: how to check which observable emitted an error

I'm using RxJava's flatmap in order to execute multiple calls in sequence where one call relys on the previous call. I also need to know which observable emitted an error in the case that onError is called in order to properly implement my error handling. How do I achieve this?
Here is my code:
mSubscription = RxUtil.callObservable(mDataManager.createAccount(email, password))
.flatMap(new Func1<AuthResult, Observable<Void>>() {
#Override
public Observable<Void> call(AuthResult authResult) {
User user = new User(0, null, null, name, null, username, 0, 0);
return RxUtil.callObservable(mDataManager.createUser(authResult.getUser().getUid(), user));
}
})
.subscribe(new Subscriber<Void>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
try {
throw (Exception) throwable;
} catch (FirebaseAuthUserCollisionException ucException) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_exists));
} catch (Exception exception) {
getPickUpView().showError(PickUpApplication.getContext().getString(R.string.error_account_general));
}
}
#Override
public void onNext(Void aVoid) {
getPickUpView().createAccountSuccessful(authResult);
}
});
I was thinking about this the wrong way. Is summary, I thought this was an issue that I needed to address when i didn't. RxJava will emit all errors in the onError method no matter what observable emits the error. Once onError is called the subscription is done, so the flatmap call will never take place.
In summary, all I need to do is handle my errors from both observables I call (the original and the one in the flatmap) in the same onError method.

Categories

Resources