I am having some trouble with executing some logic when a subscription has been unsubscribed. I've been at this for hours and I have made little progress so far. This is a simplified version of my code:
public class Command<E> {
public CommandActionObservable execute() {
final CommandAction<E> command = createCommand();
final OnSubscribe<CommandAction<E>> onSubscribe = (subscriber) -> {
/* Create a listener that handles notifications and register it.
* The idea here is to push the command downstream so it can be re-executed
*/
final Listener listener = (event) -> {
subscriber.onNext(command);
}
registerListener(listener);
/* This is where I'm having trouble. The unregister method
* should be executed when the subscriber unsubscribed,
* but it never happens
*/
subscriber.add(Subscriptions.create(() -> {
unregisterListener(listener);
}));
// pass the initial command downstream
subscriber.onNext(command);
kickOffBackgroundAction();
}
final Observable<CommandAction<E>> actionObservable = Observable.create(onSubscribe)
.onBackpressureLatest()
.observeOn(Shedulers.io())
.onBackpressureLatest();
return new CommandActionObservable((subscriber) -> {
actionObservable.unsafeSubscribe(subscriber);
})
}
public class CommandActionObservable extends Observable<CommandAction<E> {
// default constructor omitted
public Observable<E> toResult() {
return lift((Operator) (subscriber) -> {
return new Subscriber<CommandAction<E>>() {
// delegate onCompleted and onError to subscriber
public void onNext(CommandAction<E> action) {
// execute the action and pass the result downstream
final E result = action.execute();
subscriber.onNext(result)
}
}
}
}
}
}
I am using the Command in the usual way, adding the resulting subscription to a CompositeSubscription and unsubscribing from it in onDestroy(). Here is an example:
final Observable<SomeType> obs = new Command<SomeType>()
.execute()
.toResult();
subscription.add(obs.subscribe(// impl here));
public void onDestroy() {
super.onDestroy();
subscription.unsubscribe();
}
As mentioned, I can't get the unsubscription logic to work and unregister the listener, which causes memory leaks in the app. If I call doOnUnsubscribe() on obs it gets called, so I am unsubscibing correctly, but maybe the nesting of the observables and lifting causes some issues.
I'd be glad to head opinions on this one.
Turns out it was way easier than I anticipated.
After a bit of digging around I was able to come up with the answer on my own. Just posting this for people that may end up in the same situation as me.
So, as I mentioned in my question, if I added a doOnSubscribe() action to the observable I was getting in my Activity, it gets notified. Next I tried adding the same action on the inner Observables I'm creating in the execute() method. They were not getting called. So, I came to the conclusion that the chain was getting broken somewhere between the observable in my activity and the observables I was creating in execute().
The only thing that was happening to the stream was the application of my custom Operator implemented in toResult(). After a Google search, I came across this excellent article - Pitfalls of Operator Implementation. I was indeed braking the chain in my operator and the upstream observables were not notified of the unsubscription.
After I did what the author advices, all is good. Here is what I needed to do:
lift((Operator) (subscriber) -> {
// connect the upstream and downstream subscribers to keep the chain intact
new Subscriber<CommandAction<E>>(subscriber) {
// the implementation is the same
}
}
Related
I just started building a simple Android app, in which I'd like to make a network request in a background thread and then update the main thread (UI thread) with the servers response. So far I used AsyncTasks, but future implementations I'd like to use reactive Java (RxJava). I have never done reactive calls before, so I'd like to have a simple but complete example (Observable and Observer creation and subscription) upon which it is possible to further build on.
I managed to include the RxJava dependency into the basic Android project and have written a very simple main activity using AsyncTasks for the network request. Now I tried to substitute the AsyncTask implementation with a reactive one, but got stuck in all the information regarding the Observable and Observer. I'm just not sure what exactly is necessary for a minimum but fully working example.
I'd really apprechiate a bit of help in transforming the main parts into an reactive implementation, since I don't know how to handle the generation of the Observable from the response string and subscribe an Observer.
Thanks.
package com.example.reactiveTest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Button btnSend = null;
private TextView result = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.btnSend = findViewById(R.id.button_send);
this.result = findViewById(R.id.result);
}
public void onClickBtnSend(View view) {
new SomeTask().execute("Just some String");
}
class SomeTask extends AsyncTask<String, Void, String> {
#Override
protected String doInBackground(String... strings) {
// server request returning response String
return response;
}
#Override
protected void onPostExecute(String string) {
// update UI with response String
result.setText(string);
}
}
}
With AsyncTask, you're basically performing an asynchronous operation on a worker thread, then using its result on the main thread. In Rx you'd use something like the following:
Observable.fromCallable(asyncOperation)
.subscribeOn(backgroundThread)
.observeOn(mainThread)
.subscribe(result -> { /* update UI for instance */ })
It seems you're also interested in onNext, onError and onComplete.
onNext is called every time the observable emits an item. Each time it's called it receives an item, and can then process it.
onError is called when the observable has encountered an error for whatever reason. When it's called, it receives a Throwable, which represents the cause of the error. after it's called, onNext and onComplete are not called.
onComplete is called after onNext is called with the last item. It doesn't receive any input, you could do some clean up in it for example.
Using the above methods looks like this:
Observable.fromCallable(asyncOperation)
.subscribeOn(backgroundThread)
.observeOn(mainThread)
.subscribe(onNext, onError, onComplete)
[Edit]
If you'd like to create your Observable using Observable.create(), you can definitely do that, it gives you finer control over what and when you emit through the Observable. You can do this for instance if you want to handle some specific errors that can result from your network request, and emit different Throwables depending on the error.
ObservableOnSubscribe asyncOperation = new Observable.OnSubscribe<Integer>() {
#Override
public void call(Subscriber<? super Integer> emitter) {
try {
// network request
// Once result is ready, call emitter.onNext().
// When done, complete this Observable by calling emitter.onComplete()
} catch (Exception e) {
// handle error, and emit it using emitter.onError()
}
}
}
I have a plugin method that acts on remote hardware via Bluetooth.
It sends a command to the hardware, which executes some action.
After the hardware action finishes a callback defined outside of my method is called.
I only want to call CallbackContext.success(...) or CallbackContext.error(...) after the callback is called, so i want to wait for my callback to be called.
How would i go about this?
E.g. part of CordovaPlugin-class:
public void actOnHardware(CallbackContext callbackContext)
{
this.verifiyBluetoothEnabled();
this.hardwareConnection.doSomething()
// Now wait for the callback to complete before calling
// callbackContext.success() or error()
callbackContext.error("Not implemented.");
}
#Override
public void hardwareActionCallback(result)
{
// Notify actOnHardware() that we're finished.
}
This seems to be more of a Java thing, but i can't get my head to wrap around it.
Is using Object.wait() and Object.notify() a viable option or does calling wait() prevent the callback from getting called due to thread stuff? If so - how to solve this?
E.g. is it sufficient to just do:
private Object lockObj;
private boolean actionFinished;
public void actOnHardware(CallbackContext callbackContext)
{
this.verifiyBluetoothEnabled();
this.actionFinished = false;
this.hardwareConnection.doSomething()
while(!this.actionFinished)
this.lockObj.wait();
callbackContext.error("Not implemented.");
}
#Override
public void hardwareActionCallback(result)
{
this.actionFinished = true;
this.lockObj.notify();
}
Kind Regards
I have an HTTP request that triggers a long-running task (multiple HTTP requests to another service) that is supposed to be completed in the background while the original requests complete.
So what I do is
public void triggerWork(#RequestBody SomeObject somObject) {
return new ResponseEntity<>(startWorkAndReturn(somObject), HttpStatus.OK);
}
public void startWorkAndReturn(SomeObject someObject) {
Observable.create(observableEmitter -> {
// do the work with someObject here and at some time call
observableEmitter.onNext("result");
}).subscribe(new Observer<Object>() {
#Override
public void onSubscribe(Disposable disposable) {
}
#Override
public void onNext(Object o) {
// called at some unknown time
}
#Override
public void onError(Throwable throwable) {
}
#Override
public void onComplete() {
// currently not used as all the work is done in onNext but maybe that's a mistake
}
});
return;
}
But this seems to block the request until all the work has been done. Which already seems odd to me, since I never call onComplete, which in itself might be a mistake. But still, I am wondering how to create a request that immediately returns after triggering a background worker.
Is Flowables the solution here? I am going to refactor to those anyways to handle backpressure. Or do I need to create a background worker Thread? What is the best practice here?
Thanks
I would use Observable.fromCallable{} since you need emit only single event. That will handle onCompleate call. From information you share I don`t know how can you properly handle disposable. You should add subscribeOn() and observeOn() operators that will define on which thread 'work' should be processed and result should be observed.
Docs ref:
http://reactivex.io/RxJava/javadoc/io/reactivex/Observable.html#fromCallable-java.util.concurrent.Callable-
http://reactivex.io/documentation/operators/subscribeon.html
http://reactivex.io/documentation/operators/observeon.html
I know there are probably a couple ways to do this, just looking for the most efficient and concise way to go about it:
public Object giveMeNewObject() {
final Object result = null;
SomeApiClient.start(new Callback() { // starts an async process
#Override
public void onSuccess(Object somethingNew) {
result = somethingNew; //ERROR; can't set cause final
}
});
return result; //result is null, cause Async already finished
}
From your code - this is modified on fly so correct mistakes and all will work as you expect - caller will wait untill 3rd party finishes the processing and will get the result of that process:
public Object giveMeNewObject() {
CountDownLatch latch=new CountDownLatch(1);
Callback callback=new Callback() {
public sometype result=null;
#Override
public void onSuccess(Object somethingNew) {
result = somethingNew; //ERROR; can't set cause final
latch.countDown();
}
});
SomeApiClient.start(callback);
latch.await(sometimetowait);
return callback.result;
}
Read the documentation of AsyncTask. Your job should be done in doInBackground method and the result should be returned by that method. Later on you can use get(Timeout) method to retrieve that returned value. get will even block if the computation in doInBackground is not complete yet for given ammount of the time.
You can find tons of examples of how to use async task. One of them is in the API documentation (link above)
I'm using Square's Retrofit Client to make short-lived json requests from an Android App. Is there a way to cancel a request? If so, how?
For canceling async Retrofit request, you can achieve it by shutting down the ExecutorService that performs the async request.
For example I had this code to build the RestAdapter:
Builder restAdapter = new RestAdapter.Builder();
restAdapter.setEndpoint(BASE_URL);
restAdapter.setClient(okClient);
restAdapter.setErrorHandler(mErrorHandler);
mExecutorService = Executors.newCachedThreadPool();
restAdapter.setExecutors(mExecutor, new MainThreadExecutor());
restAdapter.setConverter(new GsonConverter(gb.create()));
and had this method for forcefully abandoning the requests:
public void stopAll(){
List<Runnable> pendingAndOngoing = mExecutorService.shutdownNow();
// probably await for termination.
}
Alternatively you could make use of ExecutorCompletionService and either poll(timeout, TimeUnit.MILISECONDS) or take() all ongoing tasks. This will prevent thread pool not being shut down, as it would do with shutdownNow() and so you could reuse your ExecutorService
Hope it would be of help for someone.
Edit: As of OkHttp 2 RC1 changelog performing a .cancel(Object tag) is possible. We should expect the same feature in upcoming Retrofit:
You can use actual Request object to cancel it
okClient.cancel(request);
or if you have supplied tag to Request.Builder you have to use
okClient.cancel(request.tag());
All ongoing, executed or pending requests are queued inside Dispatcher, okClient.getDispatcher(). You can call cancel method on this object too. Cancel method will notify OkHttp Engine to kill the connection to the host, if already established.
Edit 2: Retrofit 2 has fully featured canceling requests.
Wrap the callback in a delegate object that implements Callback as
well. Call some method to clear out the delegate and have it just
no-op whenever it gets a response.
Look at the following discussion
https://plus.google.com/107765816683139331166/posts/CBUQgzWzQjS
Better strategy would be canceling the callback execution
https://stackoverflow.com/a/23271559/1446469
I've implemented cancelable callback class based on answer https://stackoverflow.com/a/23271559/5227676
public abstract class CancelableCallback<T> implements Callback<T> {
private static List<CancelableCallback> mList = new ArrayList<>();
private boolean isCanceled = false;
private Object mTag = null;
public static void cancelAll() {
Iterator<CancelableCallback> iterator = mList.iterator();
while (iterator.hasNext()){
iterator.next().isCanceled = true;
iterator.remove();
}
}
public static void cancel(Object tag) {
if (tag != null) {
Iterator<CancelableCallback> iterator = mList.iterator();
CancelableCallback item;
while (iterator.hasNext()) {
item = iterator.next();
if (tag.equals(item.mTag)) {
item.isCanceled = true;
iterator.remove();
}
}
}
}
public CancelableCallback() {
mList.add(this);
}
public CancelableCallback(Object tag) {
mTag = tag;
mList.add(this);
}
public void cancel() {
isCanceled = true;
mList.remove(this);
}
#Override
public final void success(T t, Response response) {
if (!isCanceled)
onSuccess(t, response);
mList.remove(this);
}
#Override
public final void failure(RetrofitError error) {
if (!isCanceled)
onFailure(error);
mList.remove(this);
}
public abstract void onSuccess(T t, Response response);
public abstract void onFailure(RetrofitError error);
}
Usage example
rest.request(..., new CancelableCallback<MyResponse>(TAG) {
#Override
public void onSuccess(MyResponse myResponse, Response response) {
...
}
#Override
public void onFailure(RetrofitError error) {
...
}
});
// if u need to cancel all
CancelableCallback.cancelAll();
// or cancel by tag
CancelableCallback.cancel(TAG);
This is for retrofit 2.0, the method call.cancel() is there which cancels the in-flight call as well. below is the document definition for it.
retrofit2.Call
public abstract void cancel()
Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not yet been executed it never will be.
Now there is an easy way in latest version of Retrofit V 2.0.0.beta2. Can implement retry too.
Take a look here How to cancel ongoing request in retrofit when retrofit.client.UrlConnectionClient is used as client?
According to the Retrofit 2.0 beta 3 changelog via link https://github.com/square/retrofit/releases/tag/parent-2.0.0-beta3
New: isCanceled() method returns whether a Call has been canceled. Use this in onFailure to determine whether the callback was invoked from cancelation or actual transport failure.
This should make stuff easier.
I might be a bit late, but I've possibly found a solution.
I haven't been able to prevent a request from being executed, but if you're satisfied with the request being performed and not doing anything, you might check this question and answer, both made by me.