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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值