RxJava線程類型有以下幾種:
Schedulers.io() 用於網絡請求、訪問數據庫等耗時操作,線程數量無限制
Schedulers.newThread() 創建新的線程,需要慎用,用在長時間運行在後臺不會頻繁創建和銷燬
Schedulers.computation() 大量數據和圖片處理,線程有最大值的限制
Schedulers.immediate() 當前線程,可以理解爲默認的是這個玩意
AndroidSchedulers.mainThread() 把當前線程切到主線程,實現 UI 更新
Schedulers.single() 線程中的單例模式,始終是一個獨立的線程
操作符
observeOn(Scheduler scheduler)指定觀察者的線程,一般指定它上面代碼中邏輯所在的線程
subscribeOn(Scheduler scheduler)指定被觀察者線程,一般指定它下面代碼中邏輯所在的線程
上面是一個簡單的介紹,我們使用 RxJava 較多的地方,是與 Retrofit 配合使用,進行網絡請求,獲取數據後更新UI等操作,看個例子
public interface ApiService {
@FormUrlEncoded
@POST
Observable<String> post(
@Url String url,
@FieldMap Map<String, String> param
);
}
private <S> S createServiceConverterFactory(Class<S> serviceClass) {
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(15000, TimeUnit.MILLISECONDS)
.addNetworkInterceptor(new StethoInterceptor())
.readTimeout(15000, TimeUnit.MILLISECONDS)
.build();
Retrofit retrofit = new Retrofit.Builder().baseUrl("http://www.baid.com/").client(client)
.addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();
return retrofit.create(serviceClass);
}
private void reqeustMapFromSever() {
TreeMap<String,String> paramsMap = new TreeMap<String, String>();
paramsMap.put("accid", "123456");
paramsMap.put("imei", "654321");
paramsMap.put("oem", "ooom");
paramsMap.put("qid", "self");
ApiService apiService = createServiceConverterFactory(ApiService.class);
Observable<String> observable = apiService.post("http://www.baid.com/",paramsMap);
observable
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.i("observable_request", "onError "+ e);
}
@Override
public void onNext(String s) {
Log.i("observable_request", "reqeustMarqueFromSever " + Thread.currentThread().getName() + " "+ s);
}
});
}
在這個例子中,我們進行網絡請求,subscribeOn(Schedulers.io()) 上面的代碼是 apiService.post("http://www.baid.com/", paramsMap),這一步請求時是用的 io() 對應的子線程; observeOn(AndroidSchedulers.mainThread()) 下面的代碼是 subscribe(new Subscriber<String>(){}) 方法,Subscriber 中的回調時在UI線程中執行,這個調度執行順序也是從上往下執行的。再比如說,我們獲取到網絡數據後,需要進行一些數據篩選,比較耗時,怎麼辦?在 onNext() 方法中再開一條線程嗎?想想前面講的 map() 方法,這裏可以借鑑一下。再次模擬獲取報文的長度來代替數據的篩選,假如它是耗時操作
private void reqeustMapFromSever1() {
TreeMap<String,String> paramsMap = new TreeMap<String, String>();
paramsMap.put("accid", "123456");
paramsMap.put("imei", "654321");
paramsMap.put("oem", "ooom");
paramsMap.put("qid", "self");
ApiService apiService = createServiceConverterFactory(ApiService.class);
Observable<String> observable = apiService.post("http://www.baid.com/",paramsMap);
observable
.map(new Func1<String, Integer>() {
@Override
public Integer call(String s) {
Log.i("observable_request", "call " + Thread.currentThread().getName());
return s.length();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Integer>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
Log.i("observable_request", "onError "+ e);
}
@Override
public void onNext(Integer s) {
Log.i("observable_request", "reqeustMarqueFromSever " + Thread.currentThread().getName() + " "+ s);
}
});
}
這樣,就把數據轉換了,同樣還是在子線程中,並且比價優雅。這裏使用的一個心得就是不管操作符怎麼使用,也不管線程怎麼切換,接着順序是從上往下就行,想怎麼轉換數據,就在對應的地方添加自己的邏輯即可。假如說我們把 subscribeOn( Schedulers.io()) 移到了 map() 方法的上面,那 map() 中的代碼是在哪個線程中呢?記得,如果沒有明確標明,則是默認的
Schedulers.immediate(),代表當前線程,map() 上面是 apiService.post() 方法,是在子線程,因此 map() 也是在子線程中。
再比如說,項目代碼中一般會封裝一些子線程工具類,通常情況是封裝一個線程池,用線程池生成線程來調用 Runnable ,在 run() 方法中回調接口。我們用 RxJava 來封裝些方法
public class ThreadUtils {
public static void newThread(final Runnable runnable){
Schedulers.newThread().createWorker().schedule(new Action0() {
@Override
public void call() {
runnable.run();
}
});
}
public static void newThreadDelayed(final Runnable runnable, long time){
Schedulers.newThread().createWorker().schedule(new Action0() {
@Override
public void call() {
runnable.run();
}
}, time, TimeUnit.MILLISECONDS);
}
public static void ioThread(final Runnable runnable){
Schedulers.io().createWorker().schedule(new Action0() {
@Override
public void call() {
runnable.run();
}
});
}
public static void ioThreadDelayed(final Runnable runnable, long time){
Schedulers.io().createWorker().schedule(new Action0() {
@Override
public void call() {
runnable.run();
}
}, time, TimeUnit.MILLISECONDS);
}
}
這樣,都是開啓子線程執行,如果需要切換到UI線程,可以使用 Handler 來切換;如果不想在方法中使用 Handler 呢?怎麼辦?別忘了 AndroidSchedulers ,看例子
public static void ioThreadUI(final Runnable runnable, final Runnable runnableUI){
Schedulers.io().createWorker().schedule(new Action0() {
@Override
public void call() {
runnable.run();
AndroidSchedulers.mainThread().createWorker().schedule(new Action0() {
@Override
public void call() {
runnableUI.run();
}
});
}
});
}
runnable 是子線程耗時操作,而 runnableUI 是要等子線程處理完數據後刷新UI用的,這裏只是個例子,如果要實用,還需使用泛型修改一下。
public static <V> void ioThreadUI(final DataCallBack<V> callBack) {
Schedulers.io().createWorker().schedule(new Action0() {
@Override
public void call() {
final V data = callBack.call();
AndroidSchedulers.mainThread().createWorker().schedule(new Action0() {
@Override
public void call() {
callBack.refush(data);
}
});
}
});
}
public interface DataCallBack<V> {
V call();
void refush(V v);
}
調用的代碼可以這樣寫
private void test(){
ThreadUtils.ioThreadUI(new DataCallBack<String>() {
@Override
public String call() {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.e(TAG, Thread.currentThread().getName() );
return "abc";
}
@Override
public void refush(String s) {
Log.e(TAG, Thread.currentThread().getName() +" " + s);
}
});
}
看着是不有點不對勁?其實這個只是 RxJava 鏈式的拙劣變形,這裏只是爲了便於大家理解,正式使用的話還是直接使用 RxJava 封裝好的方法爲宜。至於 AndroidSchedulers 爲什麼可以切換線程到UI線程,我們看看它的源碼
public final class AndroidSchedulers {
private static final AtomicReference<AndroidSchedulers> INSTANCE = new AtomicReference<>();
private final Scheduler mainThreadScheduler;
private static rx.android.schedulers.AndroidSchedulers getInstance() {
for (;;) {
rx.android.schedulers.AndroidSchedulers current = INSTANCE.get();
if (current != null) {
return current;
}
current = new AndroidSchedulers();
if (INSTANCE.compareAndSet(null, current)) {
return current;
}
}
}
private AndroidSchedulers() {
RxAndroidSchedulersHook hook = RxAndroidPlugins.getInstance().getSchedulersHook();
Scheduler main = hook.getMainThreadScheduler();
if (main != null) {
mainThreadScheduler = main;
} else {
mainThreadScheduler = new LooperScheduler(Looper.getMainLooper());
}
}
public static Scheduler mainThread() {
return getInstance().mainThreadScheduler;
}
public static Scheduler from(Looper looper) {
if (looper == null) throw new NullPointerException("looper == null");
return new LooperScheduler(looper);
}
@Experimental
public static void reset() {
INSTANCE.set(null);
}
}
我們看看 mainThread() 方法,getInstance() 明顯是個單利,它裏面使用了 AtomicReference 保證單利對象,裏面調用了 AndroidSchedulers 的無參構造方法,構造中,RxAndroidPlugins 和 RxAndroidSchedulersHook 都是單利,它們是起到鉤子作用,這裏可以忽略不計;由於 hook.getMainThreadScheduler() 獲取的對象是 null,所以 mainThreadScheduler 的值是 new 出來的 LooperScheduler 對象,LooperScheduler 構造中傳入的 Looper 是UI線程的。from() 方法是傳入對應線程的 Looper,看看 LooperScheduler的源碼
createWorker() 方法則是 LooperScheduler 的方法
class LooperScheduler extends Scheduler {
private final Handler handler;
LooperScheduler(Looper looper) {
handler = new Handler(looper);
}
LooperScheduler(Handler handler) {
this.handler = handler;
}
@Override
public Worker createWorker() {
return new HandlerWorker(handler);
}
...
}
看的出該方法對應的是 HandlerWorker 對象,看看它的 schedule() 方法
@Override
public Subscription schedule(Action0 action, long delayTime, TimeUnit unit) {
if (unsubscribed) {
return Subscriptions.unsubscribed();
}
action = hook.onSchedule(action);
ScheduledAction scheduledAction = new ScheduledAction(action, handler);
Message message = Message.obtain(handler, scheduledAction);
message.obj = this;
handler.sendMessageDelayed(message, unit.toMillis(delayTime));
if (unsubscribed) {
handler.removeCallbacks(scheduledAction);
return Subscriptions.unsubscribed();
}
return scheduledAction;
}
這裏代碼有點精妙,我們只注意中間的代碼即可,這裏是創建了一個 ScheduledAction 對象,然後通過 Handler 來處理數據。我們看看 ScheduledAction 是什麼
static final class ScheduledAction implements Runnable, Subscription {
@Override public void run() {
try {
action.call();
} catch (Throwable e) {
...
}
}
...
}
ScheduledAction 實現了 Runnable,因此 Message.obtain(handler, scheduledAction) 方法就好理解了,我們知道 Handler 中的 dispatchMessage() 方法
Message:
public static Message obtain(Handler h, Runnable callback) {
Message m = obtain();
m.target = h;
m.callback = callback;
return m;
}
Handler:
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
可以看到,scheduledAction 封裝到了 Message 中,賦值給 callback 屬性;最終經過 Handler 的 dispatchMessage() 方法,此事 msg.callback 不爲null,執行了 handleCallback() 方法,調用了 scheduledAction 的 run() 方法,經過 Handler 後已經是在 UI 線程了。