I have the following code:
checkZipCode.exec(it)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe {
Timber.d("Main thread: ${Looper.getMainLooper().isCurrentThread}")
view.showLoading(true)
}
.doOnDispose { view.showLoading(false) }
.flatMapMaybe { isZipValid ->
if (isZipValid) {
userModel.zipCode = it.toString()
saveUser.exec(userModel)
autoSelectCityIfSingle.exec()
} else {
// TODO: Should show error here?
Maybe.empty()
}
}
.subscribe(...)
Logs are: Main thread: false
I thought that doOnSubscribe is called on the latest scheduler above it. Isn't it?
The subscription execution path isn't really an emission that observeOn acts upon. See
this question. If you really want to have something occur on the main thread in that spot in your chain, perhaps you can flatMap in another Observable that would do something within its doOnSubscribe (since that will occur on the emission thread, which is the main thread here).
Well, doOnSubscribe is executed on the same thread with subscribe().
I had problems because subscribe() was called on I/O thread.
Related
I have a problem with this block of code
val obs = PublishSubject.create<Int>()
obs
.subscribeOn(AndroidSchedulers.mainThread())
.subscribe{ value ->
Log.i("TEST", "$value")
}
(1..4).forEach { obs.onNext(it) }
When I use subscribeOn mainThread and call onNext() nothing happens,nothing printed out.. It works well with subscribeOn Schedulers.io() or Schedulers.computation()
I dont know if its because im already in main thread and call subscribeOn(AndroidSchedulers.mainThread()) but when i try calling these block of code in some random worker thread then it works well
What is the best way to synchronize threads in this case:
fun doSomething() {
readFromDB.subscribe(object : DisposableMaybeObserver<List<Trip>>()) {
override onSuccess() {
callback.complete()
}
override onFailure() {
callback.complete()
}
}
}
Two threads access this block and run into a race condition.
I need only one thread to read from DB and have that state until the callback completes.
How to lock the other thread from executing this block.
Tried using a lock / synchronized. But, how to unlock from within the onSuccess or onFailure. Does not solve the problem.
In other words, how to wait for the thread to read from DB and get back onSuccess / onFailure, for the 2nd thread to do the same.
You could confine the observation to a single-threaded scheduler. Typically, one would need to run completion code on the main thread thus:
readFromDB
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : DisposableMaybeObserver<List<Trip>>()) {
override onSuccess() {
callback.complete()
}
override onFailure() {
callback.complete()
}
}
If the target thread doesn't matter, you could also use Schedulers.single().
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
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.
I am dealing with the problem.
I am trying to call RxJava in the sync manner, however doing that results in blocking the Main thread.
Here is my code
#Override
public Single<SettingsBundle> getSettings() {
SettingsBundle settingsModel = mSettingsManager.getSettings();
return Single.just(settingsModel).map(mSettingsMapper);
}
And here is my sync call
#Override
public SettingsBundle getSettingsSync() {
return getSettings().blockingGet();
}
When calling the getSettingsSync the Main thread is blocked, however sometimes it works fine, what is more problematic.
I have tried something like that
#Override
public SettingsBundle getSettingsSync() {
return getSettings()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.blockingGet();
}
But it stills remains blocked.
What I am doing wrong, I would be grateful for any help.
Thanks.
TL;TR
never use observeOn(AndroidSchedulers.mainThread()) with blockingGet()
Long version
The output for:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val result =
Single.just("Hello")
.subscribeOn(Schedulers.io())
// .observeOn(AndroidSchedulers.mainThread())
.map {
println("1. blockingGet `$it` thread: ${Thread.currentThread()}")
return#map it
}
.blockingGet()
println("2. blockingGet `$result` thread: ${Thread.currentThread()}")
}
}
is
1. blockingGet `Hello` thread: Thread[RxCachedThreadScheduler-1,5,main]
2. blockingGet `Hello` thread: Thread[main,5,main]
As you can see result was generated on main thread (line 2), the map function was execute in the RxCachedThreadScheduler thread.
With the line .observeOn(AndroidSchedulers.mainThread()) decommented the blockingGet() never return and all is stucked.
.observeOn(AndroidSchedulers.mainThread())
.blockingGet();
The problem exists in this specific combination of operators. AndroidSchedulers schedules code to run on the main thread, however the blockingGet() stops more code from executing on that thread. Simply put AndroidSchedulers and the blocking operators of RxJava do not work well together.
Since the android scheduler might be used in the construction of the observable this means any use of the blocking* operators on the main thread will be prone to deadlocks regardless of what you try to do.
If you really need a function to run on the main thread and also need it to be synchronous, then you could do something like this:
If this is the main thread (Looper.myLooper() == Looper.getMainLooper()), then run func()
If not on the main thread, then you can use the combination of observeOn(AndroidSchedulers.mainThread()) with blockingGet()