RxJava series article directory guide:
I. Usage and Source Code Analysis of RxJava create Operator
2. Detailed usage of RxJava map operators
3. Detailed usage of RxJava flatMap operator
4. Detailed usage of RxJava concatMap operator
5. Implementation of token mechanism between app and server by RxJava onError ResumeNext operator
6. Error Retry Mechanism of RxJava retryWhen Operator
RxJava uses debounce operator to optimize app search function
8. RxJava concat operation processing multiple data sources
9. Actual usage scenarios of RxJava zip operators in Android
10. RxJava switchIfEmpty Operator Implementing Android Check Local Cache Logic Judgment
11. RxJava defer operator implements code support chain invocation
Advanced Use of combineLatest Operator
13. RxJava Causes Memory Leakage in Fragment Activity
14. interval and takeWhile Operators to Acquire Verification Codes
XV. Free switching of Rx Java threads
When Android uses RxJava, it may need to switch threads frequently, such as time-consuming operations being executed in sub-threads and rendering the interface in the main thread after execution. The following example code:
deferObservable(new Callable<String>() {
@Override
public String call() throws Exception {
//Perform time-consuming tasks
return "task result";
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
//Render View
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
});
This is the simplest logic: sub-threads process time-consuming tasks and then process results.
But in actual development, it may be more complex than this. For example, there is a logic that first loads data (sub-threads) from the local, then displays the local data (main threads) in the interface, then loads data (sub-threads) from the online, and then renders it (main threads). This requires frequent thread switching.
In RxJava, threads are switched through subscribeOn and observeOn operators. subscribeOn() mainly changes the thread of subscription, that is, the thread of call() execution, and observeOn() mainly changes the thread of sending, that is, the thread of onNext() execution.
In order to realize the logic of free switching above (sub-thread - > main thread - > sub-thread - > - > main thread)
deferObservable(new Callable<String>() { //defer observable
@Override
public String call() throws Exception {
Log.d("RxThreadFragment", "defer " + Thread.currentThread().getName());
return "task result";
}
})
.observeOn(AndroidSchedulers.mainThread())//Specify the following call to execute in the main thread
.flatMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String s) {
Log.d("RxThreadFragment", "flatMap1 " + Thread.currentThread().getName());
return Observable.just(s);
}
})
.observeOn(Schedulers.io())//Specify that the following call is executed in a subthread
.flatMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String s) {
Log.d("RxThreadFragment", "flatMap2 " + Thread.currentThread().getName());
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())//Specify that the Observable that does not specify the thread above executes in the IO thread
.observeOn(AndroidSchedulers.mainThread())//Specify the following call to execute in the main thread
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
//etc
Log.d("RxThreadFragment", s + Thread.currentThread().getName());
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
});
Output results:
defer RxIoScheduler-2
flatMap2 main
flatMap3 RxIoScheduler-3
task result main
As you can see from the above code, observeOn specifies the thread in which the adjacent Observable emits data under this operator.
subscribeOn specifies all the threads on this operator that are not named as Observable s.
For example, in the previous example, there are three observable s ("defer", "flatMap1", "flatMap2") on the subscribeOn operator.
Since "flatMap1" and "flatMap2" have been schedule d by observeOn respectively, the subscribeOn is only valid for "defer".
Let's look at another example.
final Observable<String> observable1 = RxUtils.deferObservable(new Callable<String>() {
@Override
public String call() throws Exception {
Log.e("RxThreadFragment", "observable1 thread name : " + Thread.currentThread().getName());
return "observable1 Schedulers.io()";
}
}).subscribeOn(Schedulers.io());//Specify the thread where the call method above resides
final Observable<String> observable2 = RxUtils.deferObservable(new Callable<String>() {
@Override
public String call() throws Exception {
Log.e("RxThreadFragment", "observable2 thread name : " + Thread.currentThread().getName());
return "observable2 AndroidSchedulers.mainThread()";
}
}).subscribeOn(Schedulers.io());//Specify the thread where the call method above resides
final Observable<String> observable3 = RxUtils.deferObservable(new Callable<String>() {
@Override
public String call() throws Exception {
Log.e("RxThreadFragment", "observable3 thread name : " + Thread.currentThread().getName());
return "observable3 Schedulers.io()";
}
}).subscribeOn(Schedulers.io());//Specify the thread where the call method above resides
RxUtils.deferObservable(new Callable<String>() {
@Override
public String call() throws Exception {
Log.e("RxThreadFragment", "test thread name : " + Thread.currentThread().getName());
return "test thread";
}
})
.subscribeOn(Schedulers.io())//Modify the thread above where Observable call is located
.observeOn(AndroidSchedulers.mainThread())//Modify the following thread for flatMap1 call
.flatMap(new Func1<String, Observable<String>>() {//flatMap1
@Override
public Observable<String> call(String s) {
Log.e("RxThreadFragment", "flatMap1 thread name : " + Thread.currentThread().getName());
return observable1;
}
})
.observeOn(AndroidSchedulers.mainThread())//Modify the following thread for flatMap2 call
.flatMap(new Func1<String, Observable<String>>() {//flatMap2
@Override
public Observable<String> call(String s) {
Log.e("RxThreadFragment", "flatMap2 thread name : " + Thread.currentThread().getName());
return observable2;
}
})
.flatMap(new Func1<String, Observable<String>>() {
@Override
public Observable<String> call(String s) {
Log.e("RxThreadFragment", "flatMap3 thread name : " + Thread.currentThread().getName());
return observable3;
}
})
.observeOn(AndroidSchedulers.mainThread())//Modify the following subscribe call thread
.subscribe(new Action1<String>() {
@Override
public void call(String s) {
Log.e("RxThreadFragment", "subscribe Action1 thread name : " + Thread.currentThread().getName());
}
}, new Action1<Throwable>() {
@Override
public void call(Throwable throwable) {
throwable.printStackTrace();
}
});
Output results:
Test thread name: RxIoScheduler-2 is manually set to background thread FlatMap1 thread name: main is set to main thread manually Observable 1 thread name: RxIoScheduler-3 is manually set to background thread FlatMap2 thread name: main is set to main thread manually Observable 2 thread name: RxIoScheduler-2 is manually set to background thread FlatMap3 thread name: RxIoScheduler-2 background thread Observable 3 thread name: RxIoScheduler-3 is manually set to background thread Subscribe Action1 thread name: main is set to main thread manually
As you can see from this example, flatMap3 does not set the thread it is in, and defaults to the thread mode of the previous observable, which is the thread mode of observable 2 above.
If the previous operator is not flatMap, but map (which does not return observable2), then the thread where the map call is located is used.
Through the above two examples, I believe that the Rx Java thread switching should be almost the same. It should be noted that above is basically a combination of subscribeOn and multiple observeOn s to achieve the free switching of threads.
If using multiple subscribeOns is not interesting, only the first subscribeOn is valid.