RxJava 線程切換,AndroidSchedulers 源碼分析(三)

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 線程了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章