Completable.fromAction(() -> startRecording())
.subscribeOn(Schedulers.io())
.subscribe(() -> {
boolean startSuccess = mMediaRecorder.getState() == MediaRecorder.RECORDING_STATE;
if (startSuccess) {
updateView();
startRepeatingTask();
}
},throwable -> {
Logger.info("Record failed with exception" + throwable);
}).dispose();
I am trying to execute code in background using Completable.fromAction but it is not executing the code if I use subscribeOn(Schedulers.io()).
if I remove subscribeOn(Schedulers.io()), it is executing the code in main thread. I want to executing the code in background thread.
People have already highlighted the problem with your code in the comments - you call dispose() on the Disposable that your Completable returns immediately. This means you cancel your Completable before it has even started. Alter your code to store your Disposable in an instance variable, and call dispose() only when you are no longer interested in receiving it completing. This usually happens in a lifecycle callback like onPause or onStop. For example:
public class SomeActivity extends Activity {
private final CompositeDisposable disposables = new CompositeDisposable();
//...
disposables.add(Completable.fromAction(() -> startRecording())
.subscribeOn(Schedulers.io()) //Note: `updateView` implies UI work. Should you also have `observeOn(AndroidSchedulers.mainThread)?
.subscribe(() -> {
boolean startSuccess = mMediaRecorder.getState() == MediaRecorder.RECORDING_STATE;
if (startSuccess) {
updateView();
startRepeatingTask();
}
}, throwable -> {
Logger.info("Record failed with exception" + throwable);
}));
//Later, in some other lifeycle callback when you no longer care about updates...
disposables.clear();
change the .fromAction to .fromCallable
Callables are designed to perform a single emitter, and then complete. Actual doc explanation here.
The .fromAction is a bit different. Docs here.
Related
I have the following code:
Single.create { emitter ->
// I/O thread here
ThirdPartySDK.doSomeAction {
// Main thread here
emitter.onSuccess(someValue)
}
}
.flatMap {
someOtherSingle(it) // Executes on main thread
}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({},{})
The ThirdPartySDK.doSomeAction callback posts on main thread, so the emitter will emit on the main thread too, not on the subscribe thread (and if I have some network interactions further in the flatMap, chain will fail).
If I add observeOn(Schedulers.io()) after the first Single, it switches to the correct thread, but is there any way to emit on right thread? I can't modify ThirdPartySDK behaviour.
subscribeOn
The subscribeActual lambda will be invoked on given scheduler
observeOn
Switch thread to given scheduler. Every upstream-onNext call will be called from an ObserveOn-Scheduler-Thread
As you already said, subscribeOn will only invoke the subscribeActual method call on subscribe on given Scheduler-Thread. This does not mean, that the downstream emit will be on the same thread. In your case the onSuccess emit will be called from a different thread (e.g. Database/ Http-ThreadPool etc.).
onSuccess will be called from a unknown thread (in your case main thread). The downstream call will be called from the main-thread. Therefore flatMap is called from the main-thread. Network-calls on the main-thread in the flatMap will probably fail, because it is not allowed to "block" the main-thread.
How to solve this issue?
Just place a observeOn after the Single#create. The main-thread calls onSucess. The observeOn-subscriber will get called from the main-thread. The observeOn-subscriber re-directs onSuccess downstream-call (e.g. flatMap) to given ObserveOn-Scheduler-Thread. Therefore it is given, that flatMap is called from a non main-loop thread.
Example:
#Test
fun wurst() {
val thirdPartySDKImpl = ThirdPartySDKImpl()
Single.create<String> { emitter ->
thirdPartySDKImpl.doSomeAction {
emitter.onSuccess(it)
}
}
// .subscribeOn(Schedulers.computation())
// move emit from unknown thread to computation thread
.observeOn(Schedulers.computation())
// Single.just will be subscribe from a computation thread
.flatMap { Single.just(123) }
// move onSucess/ onError emit from computation thread to main-thread
.observeOn(AndroidSchedulers.mainThread())
// subscribe onNext / onError will be called from the main-android-thread
.subscribe({}, {})
}
interface ThirdPartySDK {
fun doSomeAction(callback: (v: String) -> Unit)
}
class ThirdPartySDKImpl : ThirdPartySDK {
override fun doSomeAction(callback: (v: String) -> Unit) {
// <- impl-detail ->
callback("whatever")
}
}
NOTE: You do not need a subscribeOn, if the create-lambda does not block or does some cpu heavy stuff. If it only subscribes to a callback, which will be called from a different thread, you do not need subscribeOn.
but is there any way to emit on right thread?
You should not use any concurrency in operators. You would think, you could just do something like:
Single.create<String> { emitter ->
thirdPartySDKImpl.doSomeAction {
Schedulers.io().scheduleDirect {
emitter.onSuccess(it)
}
}
}
But this is not recommended, because you could break the serialized onNext contract^1. This example would make sure, that the onSucess downstream call would happen on expected thread, but cancellation/ unsubscription is not handled and there might be other pitfalls.
If you have a non reactive API and you want to enforce some threading-model I would suggest to wrap the sync. API with an async one and provide proper observeOn/ subscribeOn operators. Later on only use the async API.
interface ThirdPartySDKAsync {
fun doSomeAction(): Single<String>
}
class ThirdPartySDKAsyncImpl(private val sdk: ThirdPartySDK, private val scheduler: Scheduler) :
ThirdPartySDKAsync {
override fun doSomeAction(): Single<String> {
return Single.create<String> { emitter ->
sdk.doSomeAction {
emitter.onSuccess(it)
}
}.observeOn(scheduler)
}
}
Further reading: https://tomstechnicalblog.blogspot.com/2016/02/rxjava-understanding-observeon-and.html
^1 Only one thread a time is allowed to call onNext/onSuccess/onError/onComplete
I am trying to chain two network calls in my Android app. I am using Retrofit. Basically I want to do :
Make API Call to login
Wait for the response of login, save the token to SharedPrefs
Make another API call right after I've saved the token
Wait for the response, save the data
I think I have chained the stream in the right way, the only thing is I want to update the UI in between. For example once the call starts I want to display a progressDialog ( I do that in doOnSubscribe ), or dismiss the Dialog once the call has completed ( I do that in doOnComplete ). However I get the exception Only the original thread that created a view hierarchy can touch its views. I subscribe on the io thread and observe on the mainThread so that I can make the changes to the UI, however I must be missing something.
I tried adding .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
lower in the stream, but I still get the same error message.
getView().onLoginAction().subscribe(aVoid -> Observable.combineLatest(
getView().userNameObservable().map(CharSequence::toString),
getView().passwordObservable().map(CharSequence::toString),
Pair::new)
.first()
.subscribe(usernamePasswordPair -> {
User user = User.create(usernamePasswordPair.first, usernamePasswordPair.second, "");
RetrofitClientInstance.createService(AuthenticationNetworkApi.class).login(new Login(user.username(), user.password()))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(loginResponse -> {
AuthorizationResponse responseBody = loginResponse.body();
if (responseBody != null && responseBody.getAccessToken() != null && !responseBody.getAccessToken().isEmpty()) {
if (localStorage.getAccessToken().isEmpty()) {
localStorage.saveAccessToken(responseBody.getAccessToken());
}
}
}
).
doOnSubscribe( action -> getView().showProgressDialog())
.doOnError(error -> {
getView().dismissProgressDialog();
getView().showErrorMessage("Login Unsuccessful");
}).doOnComplete(() -> getView().dismissProgressDialog()
)
.flatMap(response -> RetrofitClientInstance.createService(ActivitiesApi.class).getUserActivities())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(activities -> {
for (UserActivityApiModel useractivity : activities
) {
activityService.addActivity(Activity.create(Integer.parseInt(useractivity.getId()), useractivity.getActivityName(), useractivity.getDate(),
Integer.parseInt(useractivity.getValue()), Integer.parseInt(useractivity.getSubCategory().getId())));
}
}).doOnError(error -> getView().showErrorMessage(error.getMessage()))
.doOnComplete(() -> getView().redirectToHomeScreen())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
}));
The error occurs here :
.doOnError(error -> {
getView().dismissProgressDialog();
getView().showErrorMessage("Login Unsuccessful");
})
It seems you are using a different thread to execute your backend. In that case, you can't touch the main UI thread from the second one. You need to execute first runOnUiThread { //your code }
In //your code, call the two lines of code that you put on doOnError.
I'm using Retrofit2 and RxJava2 for my network requests.
I've a simple runnable that includes:
override fun run() {
handler.postDelayed(this, interval)
DummyService.syncData(context, null)
.observeOn(AndroidSchedulers.mainThread())
.subscribe( response -> {
//nothing
}, e -> {
//nothing
});
}
And my syncData function looks like this:
fun syncData(): Single<Unit> {
Log.d("...", "sync before")
return service.test(null)
.onErrorResumeNext(ErrorHandler(BaseErrorParser()))
.map { jsonApiObject ->
Log.d("...", "sync WHOLE DATA")
....
}
}
In log I can see, that runnable works good - syncData() gets called and I see in logs "sync before" message, but I do not see "sync WHOLE DATA". I did try to place a breakpoint - it shows check (as it was there, but debugger never stops [hits] that point really). I can see that network request is executed, but it does not succeed. Why is that? If I call the very same function normally (not from runnable), it works okay.
your syncData function return Single object. you should execute it by
DummyService.syncData(context, null).subscribe(response -> {
})
I have a series of sequential Parse network calls that are all dependent on each other to save the final object all wrapped by an Observable so I can deal with everything as one call in my presenter.
If the user decides to change fragments, or leave the app or whatever, this network call is important enough that I'd like it to attempt to complete.
If I call disposables.dispose() the observable will throw an error (which I can catch) on the next .save method in the Observable and the network call does not finish.
If I don't dispose of it, the network call finishes, but it will call my onComplete and throw an error since the view is gone. I can stop that error from happening, but then I'm worried that I've created a memory leak.
I don't care if the oncomplete/onerror get called if the user gets into this situation but I would like to ensure it completes one way or another.
Is there any way to let the call complete, but not cause a memory leak by not disposing it?
fun doParseNetworkCall(){
return Observable.create<Boolean> { emitter ->
createParseObject1
createParseObject1.save()
createParseObject2
createParseObject2.add(key, createParseObject1)
createParseObject2.save()
createParseObject3
createParseObject3.add(key, createParseObject2)
createParseObject3.save()
emitter.onNext(true)
}
fun doNetworkCall(){
repo.doParseNetworkCall()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onError = { error ->
//do something
},
onComplete = {
//do something
}
).addTo(disposable)
}
I was able to resolve my issue doing the following. I think it's memory safe since the 2nd set of observables don't subscribe until the first observable completes, and if the Composite Disposable in the presenter has been disposed of already, the 2nd set will not subscribe.
repo.saveSomething()
.map {
//Do some non view stuff
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onError = { error ->
onErrorMethod()
},
onComplete = {
onSuccessMethod()
}
)
fun onSuccessMethod() {
Observable.just(true)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onComplete = {
//Do view stuff
}).addTo(disposable)
}
fun onErrorMethod() {
Observable.just(true)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onComplete = {
//Do view stuff
}).addTo(disposable)
}
I am trying to learn RxJava2, and converting my AsyncTasks to Observables.
I have the following piece of code that I am trying to convert.
if(asyncTask.getStatus() == AsyncTask.Status.RUNNING){
asyncTask.cancel();
}
asyncTask = new CustomTask();
asyncTask.execute(input);
I tried to recreate the following with Disposables.
Disposable currentTask;
PublishSubject input = PublishSubject.create();
For every input
if(currentTask != null) currentTask.dispose();
currentTask = input
.map(// Network calls
// returns CustomObject)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
// do work with result
}, throwable -> Log.e(TAG, throwable.toString()));
However, currentTask is always null. Why? Is this the wrong way to do it??
You're using Disposable correctly but I can only assume you're messing up somewhere with the subject. Subjects in rx can be both publishers and subscribers... and subjects don't necessarily wait until subscribe(...) to start emitting items. For that reason, I wouldn't suggest replacing your AsyncTasks with any kind of Subject.
You can get similar, more deterministic behavior, doing this though:
Observable<CustomObject> networkObservable =
Observable.create(emitter ->
{
try {
CustomObject object = doNetworking();
emitter.onNext(object);
emitter.onComplete();
} catch (Exception e) {
emitter.onError(e);
}
}
);
if(currentTask != null) currentTask.dispose();
currentTask = networkObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
// this next subscribe is similar to AsyncTask.execute() as it starts the stream
.subscribe(result -> {
// do work with result
}, throwable -> Log.e(TAG, throwable.toString()));
Also, consider looking into SerialDisposable and you don't have to do those null/dispose checks
SerialDisposable serialDisposable = new SerialDisposable();
Atomically: set the next disposable on this container and dispose the previous one (if any) or dispose next if the container has been disposed.
Disposable disposable = networkObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(...);
serialDisposable.set(disposable); // this will call dispose on the previous task if it exists