java进阶篇08、RxJava基本使用与原理简介

149 阅读13分钟

一、Rxjava简介

Rxjava通过链式结构实现响应式编程,提供了一系列的函数库,特别适用于异步程序线程切换及基于事件的响应编程;通过提供一系列的操作符,方便对事件进行分发、变换、响应等操作;方便进行线程的切换和调度;基本的使用方法是使用了观察者设计模式;可明显的简化代码和减少层级嵌套;

1、部分操作符或函数接口

1)、just操作符:

public static <T> Observable<T> just(T item)

创建操作符,just(T item) ... just(T item1,...,T item10)

参数为1到10个,当参数为一个时,创建ObservableJust对象,在其SubscribeActual方法中调用ScalarDisposable的run方法,然后在run方法中调用了Observer的onNext(T)方法;

当参数大于1个时,创建ObservableFromArray对象,然后在其subscribeActual方法中调用FromArrayDisposable的run方法,在run方法中循环遍历item,然后执行Observer的onNext(T)方法;

2)、map操作符:

public final <R> Observable<R> map(Function<? super T, ? extends R> mapper)

转换操作符,非静态方法说明map不能用Observable直接调用,而必须使用一个Observable对象来调用;

1、当调用map方法时,创建一个ObservableMap对象,new ObservableMap<T, R>(this, mapper),注意这个new方法中的this,他代表了上一级的Observable对象,比如这样一个链式调用:

Observable.just("aaa")
    .map(new Function<String, Object>() {
        @Override
        public Object apply(@NonNull String s) throws Exception {
            return null;
        }
    })
    .subscribe(new Observer<Object>() {
        @Override
        public void onSubscribe(@NonNull Disposable d) {
            
        }

        @Override
        public void onNext(@NonNull Object o) {

        }

        @Override
        public void onError(@NonNull Throwable e) {

        }

        @Override
        public void onComplete() {

        }
    });

我们首先调用了just操作符,然后再调用map操作符,那么这个this就代表了ObservableJust对象;然后我们去调用subscribe方法订阅Observer时,在subscribe中调用真正执行订阅工作的subscribeActual方法,也就是调用了ObservableMap的subscribeActual方法;

2、在ObservableMap的subscribeActual方法中调用了source.subscribe(new MapObserver<T, U>(t, function));这个source就是步骤一中说的this,也就是ObservableJust,所以又会调到ObservableJust的subscribe方法,也就又调用了subscribeActual,其中会创建ScalarDisposable对象,然后调用的run方法,创建ScalarDisposable对象时传入的Observer就是MapObserver,然后在run方法中调用了Observer的onNext方法,也就是调用了MapObserver的onNext方法,其中会执行这么一步操作U v = mapper.apply(t),将上一步中传下来的t转换为v,最后调用actual.onNext(v);这个actual就是我们最终实现的匿名内部类Observer对象;

3)、ObservableTransformer接口

只有一个抽象方法: ObservableSource apply(@NonNull Observable upstream);此方法可以将传入的Observable进行一系列的操作,然后再传出去;

4)、compose方法

接受一个ObservableTransformer对象:

public final <R> Observable<R> compose(ObservableTransformer<? super T, ? extends R> composer);方法的实现很简单,return composer.apply(this);注意这个this是上一级传下来的Observable;

可以通过compose方法和ObservableTransformer接口封装很多通用的操作,比如说线程的切换,比如定义一个线程切换方法:

public static <UD> ObservableTransformer<UD, UD> changeThread(){
    return new ObservableTransformer<UD, UD>() {
        @Override
        public ObservableSource<UD> apply(@NonNull Observable<UD> upstream) {
            return  upstream.subscribeOn(Schedulers.io())    
            .observeOn(AndroidSchedulers.mainThread());
        }
    };
}

然后就可以在compose方法中调用这个线程切换方法进行线程的切换。

5)、subscribe方法

此方法实现了Observable和Observer的订阅关系,可以传入一个Observer观察者对象,之所以是被观察者Observable去subscribe观察者Observer,是因为可以保持链式调用的结构;

2、Rxjava配合retrofit访问网络

实现的方法就是在retrofit的api接口定义方法的时候将返回值修改为Observable类型,这样我们通过api接口中的方法请求网络时返回的对象就可以作为Rxjava的事件源,然后进行线程切换和实现我们自己的Observer处理结果;

线程切换主要使用两个函数,subscribeOn(Scheduler)方法用于将调用此方法之前的线程切换为Scheduler线程;observerOn(Scheduler)方法用于将调用此方法之后的线程切换为Scheduler线程;

Scheduler分类:Schedulers.io()一般用于io密集型任务;Schedulers.computation()一般用于cpu密集型任务;AndroidSchedulers.mainThread()是指的安卓ui线程;如果要进行ui操作则需要切换到此线程,并且此线程不能进行耗时操作;

3、按键防抖+网络请求操作

RxView.clicks(button) .throttleFirst(2000, TimeUnit.MILLISECONDS) // 2秒钟之内 响应你一次 .subscribe(new Observer(){})

在防抖调用了一次subscribe,然后如果需要在button按下时访问两次网络,并且第二次网络的请求需要使用第一次的请求结果,这样的话还需要再嵌套两层subscribe,操作嵌套层次过深,代码的可读性不高,可以使用flatMap操作符消除这种嵌套;

flatMap操作符:public final Observable flatMap(Function<? super T, ? extends ObservableSource<? extends R>> mapper)

flatMap相较于map的区别就是,Function接口中的第二个泛型变为了ObservableSource<? extends R>,我们可以在apply函数中调用Observable.fromIterable(projectBean.getData())方法多次发送事件,从而减少嵌套层级;

4、doOnNext操作符

如果我们有一个这样的需求,需要先注册然后更新UI,然后再登录再更新UI;我们当然可以将注册和登录分成两部分来写,但是使用doOnNext操作符就可以将两个操作合并成一个;

public final Observable doOnNext(Consumer<? super T> onNext)

doOnNext接受一个Consumer(简化版的Observer),返回一个Observable,从而可以是我们的注册和登录操作合并起来,实现方式更加优雅;

5、Rxjava资源的释放

我们一般在activity的onDestory方法中进行资源的释放,在Observer的onSubscribe方法中将disposable进行赋值,然后再执行下面的操作:

if (disposable != null && !disposable.isDisposed()) disposable.dispose();

二、Rxjava模式与原理

1、观察者设计模式

观察者设计模式一般由四部分组成,被观察者抽象层、被观察者具体实现层、观察者抽象层、观察者具体实现层,一般在被观察者具体实现层中维护一个集合用来存放所有的观察者对象,当被观察者发生改变后,通知每一个观察者;例如微信公众号可以理解为一个被观察者,所有订阅该公众号的用户为观察者,当有新文章推送时,所有用户都会受到通知;

2、Rxjava全局hook

当我们去调用各种操作符时,会发现在new出Observablexxx对象之后,会使用RxJavaPlugins.onAssembly进行一层包装,在此方法中我们发现如果在RxJavaPlugins中的onObservableAssembly对象不为空,会进行一次apply转换,onObservableAssembly是Function类型的变量,默认为null,我们可以通过调用RxJavaPlugins.setOnObservableAssembly方法给onObservableAssembly自定义值,从而可以实现全局监听,这样每一处使用rxjava操作符的地方都会调用一次,我们还可以针对特定的Observablexxx对象进行特殊的处理;onObservableAssembly是静态的,全局唯一;

static volatile Function<? super Observable, ? extends Observable> onObservableAssembly;

3、RxJava的观察者模式

rxjava主要通过Observable、Observer和subscribe过程三个关键部分实现观察者模式的,在创建Observable的过程中,可以使用多个操作符进行链式调用,并且只有subscribe订阅调用之后,被观察者才会收到信息;所以说rxjava的观察者模式更像是发布订阅模式,只有观察者订阅了才会起作用,而且可以理解为被观察者和观察者之间是多对一的关系,因为被观察者Observable的创建过程时多次链式调用,而观察者Observer只有一个,也就是我们自定义的Observer或者Consumer;而传统的观察者设计模式是一对多的关系;

4、create创建操作符

public static Observable create(ObservableOnSubscribe source)

在我们调用create操作符时需要传入一个ObservableOnSubscribe对象,我们可以先称之为自定义source,它是一个单方法接口,只有一个subscribe方法;调用create之后会返回ObservableCreate对象,在构造方法中将自定义source传入,然后在真正执行订阅操作的subscribeActual方法中创建一个CreateEmitter发射器,此发射器会持有我们自定义的Observer;然后首先调用observer.onSubscribe方法,然后以发射器为对象调用自定义source的subscribe方法,因此需要我们自己在自定义source的subscribe方法中调用emitter.onNext和emitter.onComplete方法,最终发射器的emitter.onNext和emitter.onComplete方法会调用我们自定义Observer的对应方法;

5、RxJava的装饰模型

当我们链式调用各种操作符时,rxjava会使用装饰模型将每一层都封装起来;例如我们先调用create操作符,然后调用了两次map操作符,则会首先创建ObservableCreate对象,然后以ObservableCreate为source创建ObservableMap对象,最后再以第一次的Observablemap为source创建第二层ObservableMap,这样当我们一旦调用subscribe方法之后,就会调用到map的subscribeActual方法中,而在此方法中调用的是source.subscribe,从而层层拆解,就会从上到下调用各级的subscribeActual方法;

三、RxJava线程切换和自定义操作符

1、subscribeOn(Schedulers.io())

用于将线程切换到IO线程执行:

1)、Schedulers.io():调用此方法经过一些列的全局hook和callable转换,最终得到一个IoScheduler;

2)、subscribeOn(IoScheduler):调用这个方法才是真正的进行线程切换,将IoScheduler作为参数传入;返回一个ObservableSubscribeOn对象,在ObservableSubscribeOn的subscribeActual方法中调用了scheduler.scheduleDirect(Runnable),在此方法中又会调用

Worker w = createWorker();
w.schedule(task, delay, unit);

这个task实际就是我们传入的runnable进行封装之后获得的,然后这个w是通过createWorker()获得,createWorker()在Scheduler中是一个抽象方法,所以具体的实现在IoScheduler中,会返回一个EventLoopWorker;

public Worker createWorker() {
    return new EventLoopWorker(pool.get());
}

然后调用EventLoopWorker的schedule方法;

public Disposable schedule(@NonNull Runnable action, long delayTime, @NonNull TimeUnit unit) {           
    return threadWorker.scheduleActual(action, delayTime, unit, tasks);
}

又会调用到threadWorker.scheduleActual方法,这个threadWorker是NewThreadWorker类型的;

public ScheduledRunnable scheduleActual(final Runnable run, long delayTime, @NonNull TimeUnit unit, @Nullable DisposableContainer parent) {
    Runnable decoratedRun = RxJavaPlugins.onSchedule(run);
    ScheduledRunnable sr = new ScheduledRunnable(decoratedRun, parent);      
    if (delayTime <= 0) {
        f = executor.submit((Callable<Object>)sr);
    } else {
        f = executor.schedule((Callable<Object>)sr, delayTime, unit);
    }
    sr.setFuture(f);           
    return sr;
}

可以看到在这个方法中有调用了executor.submit或者executor.schedule,那么这个executor是什么呢?executor的创建过程如下所示:

public static ScheduledExecutorService create(ThreadFactory factory) {
    final ScheduledExecutorService exec = Executors.newScheduledThreadPool(1, factory);            
    return exec;
}

可以看到其实就是使用Executors.newScheduledThreadPool(1, factory)创建了一个核心线程为1的线程池;也就是将我们封装的runnable交给了这个线程池执行,从而完成了线程切换;那么我们刚开始封装的Runnable在哪呢?其实就在ObservableSubscribeOn的subscribeActual方法中,也就是这部门new SubscribeTask(parent),这个Runnable中的run方法如下所示:

public void run() {
    source.subscribe(parent);
}

也即是我们把这个run方法放入线程池执行了,这个source就是上层的代表,这个parent就是我们对下层Observer的封装,所以说如果仅仅只调用了这个方法进行线程切换,那么从上往下都是在线程池中执行的,所以说在执行我们自定义的Observer之前需要使用observeOn将线程切回ui线程,否则程序崩溃;

2、.observeOn(AndroidSchedulers.mainThread())

调用此方法将线程切换为安卓ui线程:

1)、AndroidSchedulers.mainThread():这个方法跟io调用过程差不多,也是经过好几次全局hook及callable的转换,最终会得到HandlerScheduler:

static final Scheduler DEFAULT = new HandlerScheduler(new Handler(Looper.getMainLooper()));

2)、observeOn:

调用此方法才是真正的线程切换,会返回ObservableObserveOn对象,ObservableObserveOn的subscribeActual方法:

protected void subscribeActual(Observer<? super T> observer) {       
    Scheduler.Worker w = scheduler.createWorker();
    source.subscribe(new ObserveOnObserver<T>(observer, w, delayError, bufferSize));        
}

首先会调用scheduler.createWorker(),这个方法是抽象方法,也就是调用到HandlerScheduler,而在HandlerScheduler的createWorker()方法中会以handler为参数new一个HandlerWorker对象,这个handler就是第一步中的new Handler(Looper.getMainLooper()),也就是主线程对应的handler;

然后接着调用subscribeActual中的第二部分,会调用到ObserveOnObserver的onNext方法,在onNext方法中只是单纯的调用了一下schedule()方法,在schedule方法中调用 worker.schedule(this);将this作为runnable传入,这个worker也就是HandlerWorker;所以此方法就会调用到handlerworker的schedule方法:

@Override
public Disposable schedule(Runnable run, long delay, TimeUnit unit) {
    run = RxJavaPlugins.onSchedule(run);
    ScheduledRunnable scheduled = new ScheduledRunnable(handler, run);

    Message message = Message.obtain(handler, scheduled);
    message.obj = this; // Used as token for batch disposal of this worker's runnables.

    handler.sendMessageDelayed(message, Math.max(0L, unit.toMillis(delay)));
    return scheduled;
}

可以看到在schedule方法中将我们传入的runnable进行了两层封装,然后通过handler.sendMessageDelayed将message传入主线程,这个message的callback成员变量就是封装的runnable;所以handler处理的时候就会直接执行原始runnable的run方法;原始runnable也就是通过自定义Observer封装的ObserveOnObserver,在其run方法中调用了drainNormal():

void drainNormal() {
    final Observer<? super T> a = actual;
    for (;;) {
        for (;;) {
            a.onNext(v);
        }
    }
}

将与主流程无关的代码全部去掉,发现此方法就是调用了actual.onNext,这个actual通过构造方法赋值,也就是我们自定义的observer或者自定义observer的封装;

3、实现自定义操作符:模仿按钮防抖

public ObservableViewClick(View view) {
    this.view = view;
}

@Override
protected void subscribeActual(Observer<? super Object> observer) {

    MyListener myListener = new MyListener(view, observer);
    observer.onSubscribe(myListener);

    this.view.setOnClickListener(myListener);
}

核心逻辑如上所示,自定义一个ObservableViewClick实现自Observable,然后在其subscribeActual方法中创建一个封装的lisener,将view和我们自定义的observer传入,这个listener实现点击接口,这样一点调用了subscribe方法,就会回调到listener中实现的onclick方法,我们在onclick方法中调用observer.onNext即可;

static final class MyListener implements View.OnClickListener, Disposable {

    private final View view;
    private final Observer<Object> observer;  // 存一份 下一层

    private final AtomicBoolean isDisposable = new AtomicBoolean();

    public MyListener(View view, Observer<Object> observer) {
        this.view = view;
        this.observer = observer;
    }

    @Override
    public void onClick(View v) {
        if (!isDisposed()) {
            observer.onNext(EVENT);
        }
    }

    @Override
    public void dispose() {
        // 如果没有中断过,才有资格,   取消view.setOnClickListener(null);
        if (isDisposable.compareAndSet(false, true)) {
            // 主线程 很好的中断
            if (Looper.myLooper() == Looper.getMainLooper()) {
                view.setOnClickListener(null);
            } else { // 主线程,通过Handler的切换
                AndroidSchedulers.mainThread().scheduleDirect(new Runnable() {
                    @Override
                    public void run() {
                        view.setOnClickListener(null);
                    }
                });
            }
        }
    }

    @Override
    public boolean isDisposed() {
        return isDisposable.get();
    }
}

随笔记录

1、== 与 equals

==是比较的两个对象的地址,而equals在不重写的情况下也就是调用的==,所以比较两个对象需要重写equals

1)、比较两个int对象:==返回true;无equals方法;
2)、比较两个Interger对象:

	1、如果采用直接赋值或者Integer.valueOf()的方式:

		1)、进行==比较时分两种情况

		如果值大于-128小于127,则返回true,因为此时使用常量池,地址引用相同;
                但是如果值不在这个区间内,则返回false,因为此时使用new Integer的方式新建的对象,地址肯定不相同

		2)、进行equals比较时返回true

	2、如果采用new新建的方式:

		==返回falseequals返回true
3)、比较两个String对象

    如果采用new新建的方式:
        ==返回falseequals返回true;
    如果采用直接赋值的方式:
        ==返回trueequals返回true

2、子线程切换UI线程

runOnUiThread

其实内部也是调用的handler,在activity中直接new了handler,然后将runnable作为参数传递给handler的post方法

handler

主线程创建handler,不要使用匿名内部类的形式创建,容易引起内存泄漏;应该使用静态内部类,然后在静态内部类中持有对activity的弱引用,便于访问activity中的对象;第二种方式是在创建handler的时候使用带callback参数的构造方法,然后在callback中处理message