In Why is my timer Observable never called?
#Miguel Lavigne says:
"Keep in mind that if you're using Observables from within a Fragment or an Activity you should always make sure you unsubscribe from your Observables to eliminate chances of memory leaks."
It is clear to me how it works as long as I am using an Observable in an Activity, Fragment or View. But what if I am using it where there is no context?
My situation: I am having an external library which holds an object model. Each object has a .save() function, which is called from the UI. In save, an API endpoint call is performed by an Observable asynchronously.
Example:
public Overlay save() {
Observable.create(new Observable.OnSubscribe<Overlay>() {
#Override public void call(Subscriber<? super Overlay> subscriber) {
try {
Overlay overlay= OverlayEndpoint.insertOverlay(this); // call the API endpoint here
subscriber.onNext(overlay);
subscriber.onCompleted();
} catch (IOException e) {
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Overlay>() {
#Override public void call(Overlay overlay) {
// process the saved result [omitted for brevity]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
// put the overlay into a local upload queue in case the endpoint is unreachable [omitted]
}
});
return this; // return the call immediately
}
The Observable is for one-time use within save and becomes obsolete thereafter. How can I make sure it does not persist?
Situation 1: Normal unsubscribe. Is there a way to unsubscribe right from within the call(), once processing is complete?
Situation 2: For whatever reason the Observable stays in memory. Could I use .timeout() to ensure the Observable is destroyed after enough time has passed?
Situation 1: Normal unsubscribe. Is there a way to unsubscribe right from within the call(), once processing is complete?
In your case, Observable will be in memory before Action1<Overlay> or new Action1<Throwable> is called. But after one of them is called, GC should be able to clean the Observable.
Situation 2: For whatever reason the Observable stays in memory. Could I use .timeout() to ensure the Observable is destroyed after enough time has passed?
In your case, Schedulers.newThread() will create a new Thread to run the Observable.OnSubscribe<Overlay>.call. So if this method has not returned yet, such as OverlayEndpoint.insertOverlay will run about 10 minutes, Observable can not be cleaned by GC because this thread is still using it. There is nothing we can do unless there is an approach to cancel OverlayEndpoint.insertOverlay.
Related
I use Retrofit and RxJava for network calls. For the first time I ran into a weird problem. For one of the calls the following error message is displayed:
HTTP FAILED: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
But the call is started from the main thread and it's the same result even if I remove any calls in onNext. So it must be something in the call, which is a solo call! This is the call in a presenter:
public void updateEmail(final String newEmail) {
disposables.add(AccountRepository.updateEmail(newEmail)
.retry(1)
.subscribeWith(new DisposableObserver<Reply>() {
#Override
public void onNext(Reply reply) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
}));
}
And this is the Repository call:
public static Observable<Reply> updateEmail(String email) {
return getMyApiService().updateEmail(email)
.subscribeOn(Schedulers.from(AsyncTask.THREAD_POOL_EXECUTOR))
.observeOn(AndroidSchedulers.mainThread());
}
And the Retrofit interface called MyApiInterface:
#FormUrlEncoded
#POST(UrlMap.apiProfileUpdateEmailUrl)
Observable<Reply> updateEmail(#Field("email") String email);
Nothing unusual, I have more than 100 calls like this.
Now if I add retry(1) as above, the call goes through, but the first call seem to be leaked, because it's still in the threads in the Network Profiler.
Furthermore, The call happens in a Fragment in an Activity with bottom navigation. If I switch to another fragment, all the calls there finish in the background without error, but the UI is not updated, I see a blank screen. But if I navigate to another activity and back, the UI is updating again.
The disposables is a CompositeDisposable object and it's cleared when the presenter is unbound. Disposable.clear in onNext() doesn't help.
I added five interceptors to the client for logging, headers, etc. Maybe they cause this somehow? Or something else? I'm trying to fix this for more than a day, but couldn't get much closer to the solution.
There is a listener for user verification changes -- that are coming from all the response headers -- in the main feed, so we can obfuscate the images. I wrapped the listener in a runOnUiThread() method and the leak went away.
I have a function that creates Observable:
void getData() {
Observable.create(emitter -> {
// call webservice
.......
emitter.onNext(myData);
}).subscribe(data -> {
// Process data here
});
}
I don't want to use Disposable here. I think the observable is local variable, so it will be released after the function is done.
Is the observable released automatically after I call getData() function?
Observable will automatically dispose they called onComplete() or onError()
Ex: You have a method to load exactly data from 10 files Observable<String> loadFiles() which return Observable.create(). Then every time you want to emit value you call e.onNext(), after count 10 times you will call e.onComplete() to mark that your Observable has finish it's work, then it will auto dispose.
You only need to call dispose() method to indicate that the Subscriber is no longer interested in any of the Observables it is currently subscribed to. Those Observables can then (if they have no other interested observers) choose to stop generating new items to emit.
Call dispose() when activity stopped to make sure that no more emission will come after that. So it's a good practice because it can prevent memory leaks and waste of resources, network calls.
Observables do not dispose them-selfs.
It's a good practice to dispose your observable to avoid memory leaks and crashes of your app.
you either use disposable.dispose() or compositeSubscribtion.clear().
I have made a simple test and after I exited the app(back btn) observable continued to emit data.
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Observable.create(emitter -> {
for (; ; ) {
emitter.onNext("data");
Thread.sleep(3000);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).
subscribe(data -> {
Log.d(tag, (String) data);
});
}
});
OUTPUT :
onStart() is called
onResume() is called
data
data
onPause() is called
onStop() is called
data
data
I have an async method makeRequest() with callback. It called many times from different classes of my application. I need that this calls start one by one and never simultaneously.
I want to implement this using Rx. Like this:
public void execute() { // This method called many times from another classes
Observable.just(true)
// what I need to add here?
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(o -> {
internalExecute();
return o;
})
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
// here is I handle request finish
}
But at now all requests works at parallel. What I need to add here to run requests one by one?
According to comments, you have here separated streams and requests. each client that execute request expect a result from the request. but no requests allowed to run in parallel, in this case I think the easiest way is to limit the Scheduler to an application global background sequential thread Executor, i.e:
Schedulers.from(Executors.newSingleThreadExecutor())
provide somewhere in your app this single thread Executor, in singleton manner of course, it's important that each request stream will use the same object:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public void execute() { // This method called many times from another classes
Observable.just(true)
.map(o -> {
internalExecute();
return o;
})
.subscribeOn(singleThreadScheduler)
.subscribe();
}
private void internalExecute() { // This method should called only when previous call was finished
makeRequest(this::onRequestFinished);
}
private void onRequestFinished() {
//NOTE: you should make sure that the callback execute where you need it (main thread?)
// here is I handle request finish
}
besides that, you're not exposing Observable outside, to the clients, but rather using callback mechanism, you can leverage reactive approach further, by making execute() returning Observable. (and enjoy composition of Obesrvables, operators, proper use of observeOn/subscribeOn, error handling with onError, disposing/unsubscribing etc.), as you're using async api, you can use fromEmitter()/create() (in newer RxJava1 version)), read more here:
private final Scheduler singleThreadScheduler = Schedulers.from(Executors.newSingleThreadExecutor());
public Observable<Result> execute() { // This method called many times from another classes
return Observable.fromEmitter(new Action1<Emitter<? extends Object>>() {
#Override
public void call(Emitter<?> emitter) {
emitter.setCancellation(() -> {
//cancel request on unsubscribing
});
makeRequest(result -> {
emitter.onNext(result);
});
}
})
.subscribeOn(singleThreadScheduler)
}
I'm learning RxJava so please be gentle. I've watched the tutorials, done the reading, searched SO, however, I'm still having some problems transforming my AsyncTaskLoader. For some reason, I can't find a pattern of operators to achieve my task (although I think it's a common one). What I'm trying to do is the following: return an Observable my fragment could subscribe to. The observable should do the following on subscribe:
1) Fetch data from the local database by doing 2 queries, running some logic and returning results;
2) Fetching data from API;
3) Synchronising the new API data with the database;
4) Repeating step one and returning results;
So far I've transformed my db calls and my API calls to return observables. I'm trying to understand how I can emit the cold results and continue with the chain. I could probably keep the two operations separately, and use the same subscriber to subscribe to both? But I'm not sure how that would work if my new loader-replacement class returns an observable... Also I don't really need to process the results from the second observable - I just need for the first one to replay when the second one finished.
So far I have the following:
public Observable<StuffFetchResult> getColdStuff() {
return Observable.zip(mDataSource.listStuff(), mDataSource.listOtherStuff(),
(stuff, moreStuff) -> {
List<Stuff> mergedList = new ArrayList<>();
// do some merging stuff
return new StuffFetchResult(mergedList);
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
Assume I also have getHotStuff() that will do the API call and the synchronisation with the database, if that's the right approach, and return the same Observable. However, I'm stuck on the next step - how can I restart the first observable to replay once hotStuff has completed, without adding another subscriber?
EDIT:
I've made some progress and I think all I need now is to join it all up. I have my two methods:
1) getColdStuff() is pretty much as described above
2) getHotStuff() will do call to the API, synchronise with the database, and return an Observable. The idea was to call getColdStuff() again after getHotStuff() has finished in order to refresh the UI, so actual result returned from getHotStuff() can be ignored. All it needs to do is to trigger getColdStuff() once done.
I've tried the suggestion in the answer to and created the following:
BehaviorRelay<Observable<StuffFetchResult>> callSequence = BehaviorRelay.create();
Observable<StuffFetchResult> valueSequence = Observable.switchOnNextDelayError(callSequence.toSerialized());
valueSequence.subscribe(new Subscriber<StuffFetchResult>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(StuffFetchResult result) {
// UI stuff
}
});
callSequence.call(loader.getColdStuff());
I can subscribe to valueSequence here and use callSequence.call(loader.getColdStuff());, which will run the first method and produce results in onNext() of my subscription, which I can use for my UI. However, I'm not sure how to run getHotStuff() in parallel and also do a different action on it when it returns. Also getHotStuff() returns a different type of Observable so I can't really use the same callSequence?
EDIT 2
Using two subscribers, I can achieve the required behaviour I think. Not really sure if that's the right way to go about it though.
loader.getHotStuff()
.subscribeOn(Schedulers.io())
.subscribe( new Subscriber<Object>() {
#Override
public void onCompleted() {}
#Override
public void onError(Throwable e) {}
#Override
public void onNext(Object stuffWeDontCareAbout) {
callSequence.call(loader.getColdStuff());
}
});
if i understand your scenario correctly, you may want something like that -
BehaviorSubject<Observable<T> callSequence = BehaviorSubject.create();
Observable<T> valueSequence = Observable.swithOnNextDelayError(callSequence.toSerialized());
your subscriber will be listening to the valueSequence, and whenever you need to "restart", you will call this -
callSequence.onNext(call.cache()); // *call* is Observable<T>
(i leave the .subscribeOn/.observeOn configuration to you)
I am using RxJava to move network access to a separate thread in Android, but my UI still blocks.
I am not using the wrong observable as shown here: Android RxJava, Non Blocking?
The codepoints [A], [B] and [C] in below code are passed in the order [A] -> [C] -> [B] so the current thread is processed fine and RxJava calls [C] once it had a result. This is fine.
Also, blocking is much better compared to doing the network call on the UI thread, but I still have minor blocking. The UI stays fluent after the call is made, but if the server does not respond in a matter of milliseconds, it blocks.
private search; // search is an instance variable in the same class
// [A]
Observable.just(search.find("something")) // search.find calls the REST endpoint
.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Search>() {
#Override public void call(Search search) {
// further processing // [B]
}
}, new Action1<Throwable>() {
#Override public void call(Throwable throwable) {
// error handler
}
});
// [C]
Could it be a problem that search is an instance variable in the same class where the Observable uses it, but the endpoint call is performed from a separate library? It shouldn't matter, right?
Am I doing anything bad that I shouldn't be doing?
--
Find looks like this (removed exception handling for brevity):
public Search find(String searchtext) {
setSearchtext(searchtext);
SearchEndpoint.find(Session.getUser().getId(), searchtext);
return this;
}
SearchEndpoint like this:
public static Search find(final Long userId, final String searchtext) throws IOException {
return ApiService.api().searches().find(userId).setFind(searchtext).execute();
}
and makes a call to the generated Google cloud endpoint library.
Try this:
Observable.create(new Observable.OnSubscribe<Search>() {
#Override
// method signature is from memory - I hope I am correct...
public void call(Subscriber<? super Search> subscriber) {
try {
Search search = search.find("something");
subscriber.onNext(search);
subscriber.onCompleted();
} catch (SomeException e) {
subscriber.onError(e);
}
}
})
// and then continue with your .subscribeOn(...)
To clarify, maybe this makes the problem with your code more obvious:
Observable.just(search.find("something"))
is clearly equivalent to
Search search = search.find("something");
Observable.just(search)
And this makes it obvious that search.find is executed before we ever hand the control over to rxjava and it is executed on whatever thread you are currently on - then the construction of an Observable from the pre-computed value and the delivery of the value happen on another thread but that does not help you much...
I know this is a few months old-- but instead of createing an entirely new Observable (which is relatively error-prone), you can use the map operator to run the search:
String search_input = "something"; // this is where you can specify other search terms
Observable.just(search_input)
.map(s -> search.find(s)) // search.find calls the REST endpoint
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( // your subscriber goes here
If not using lambdas, that map function should look like:
.map(new Func1<String, Search>() {
#Override
public Search call(String s) {
return search.find(s)
}
})