转载

RxJava:自己动手撸一个RxBinding(一)。

RxJava:自己动手撸一个RxBinding(一)。

最近在项目中使用到了 JakeWharton 大佬的 RxBinding ,在某个表单验证的模块中发现运用 RxBinding 能得到很好的效果,既减小了代码量,逻辑也更清晰。感叹它的神奇之处,于是查看了下源码,发现源码并不是很复杂,于是决定跟着源码自己动手撸一个简易版 RxBinding ,加深对 RxJava 的理解。

预计实现两个目标:

  • View点击事件RxJava 形式实现
  • TextView 以及 子类 的的 文字变化RxJava 实现

先实现目标一。 大致思路: 为view设置监听器,当每次点击事件到来时,向下游发送事件。

我们看下原版RxBinding是怎么实现点击事件订阅的。

RxView.clicks(tv_xx).subscribe({
        ....dosomething()
        })

可以很清楚的知道, RxView.clicks(View view) 这个方法返回的是一个可被订阅的 observable ,我们看参数传入了需要被订阅点击事件 view 的引用,可以猜想这个 observableview 肯定有某种py,先不管。

依样画葫芦,原版 RxBinding 是通过静态方法返回可被订阅的的 observable ,我们也照着写就行了,本着学习的心态,不必在这些地方上纠结,重要的是拟清思路。

public class RxView {

    /**
     * 这里肯定是返回一个可被订阅的observable
     * @param view 需要被订阅点击事件的view
     * @return
     */
    public static Observable clicks(View view) {
        return null;
    }

}

代码很简单,就是一个静态方法返回一个 observable ,因为我们还没写对应的 observable ,所以这里暂时先返回null。

接下来就要写我们的 observable 了,首先我们的 observable 肯定是和 view 有着密不可分的关系的,所以肯定得持有 view 的引用,所以先这样写: 新建一个 observable ,命名为 ViewClickObservable ,继承自 Observable ,并在构造中传入 view ,然后保存.

public class ViewClickObservale extends Observable {

    private View mView;

    public ViewClickObservale(View mView) {
        this.mView = mView;
    }

    /**
     * 继承Observable必须实现的抽象方法
     * @param observer
     */
    @Override
    protected void subscribeActual(Observer observer) {

    }

}

我们看到代码中多了个 subscribeActual(Observer observer) 方法,这是 Observable 类中的抽象方法,必须重写,该方法在 observable 被下游订阅时会被调用。而参数传入的 observer ,就是我们的发射器了。有的人可能这里有疑问了, observer 不是观察者吗,怎么又变成发射器了,发射事件不是被观察者做的事情吗?如果以以往的固定思维,到这里是有点转不过圈子。但是如果你深入过 RxJava ,你会发现,它只不过是通过 Java 语法的各种包装,链式调用,形成了一种响应式风格而已.举个例子,如果你平常使用 RxJavaObserveronNext() 方法中收到一个事件,你脑子中形成的模型肯定是上游发射了一个事件到下游,然后 observe r在 onNext() 接受到了这个事件,可是本质上却是内部调用了最上游的 observeronNext() 方法而已,这里之所以说最上游的 observer 是因为中间可能用到各种操作符,而这些操作符起到的作用就是对最下游的 observer 进行层层包装改造,或者限制,最终传到源头的 Observable ,然后用这个被包装改造后的 observer 发射事件,因为每个操作符的包装改造效果不同,所以下游能收到不同的效果.

大致知道了原理,我们就可以继续改造我们的 ViewClickObservale 类了,我们知道了传入的 observer 为发射器,那么事件从哪里来呢?肯定还是只能从点击事件了.每次收到一个点击事件,我们就向下游发射一个事件就行了,继续加代码.

public class ViewClickObservale extends Observable<Object> {

    private View mView;

    public ViewClickObservale(View mView) {
        this.mView = mView;
    }

    /**
     * 继承Observable必须实现的抽象方法
     * @param observer
     */
    @Override
    protected void subscribeActual(final Observer observer) {
        
        //为view设置监听,每次收到点击事件,向下游发射事件
        mView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                observer.onNext("onClick");
            }
        });
    }

}

这是编译器提示未给 Observer 指定泛型,可能存在隐患的安全问题.我第一次看到这个提示是这想的,既然是点击事件,下游不关心事件本身,发送一个 "onClick" ( String 类型)就行了,因为 StringObject 子类,所以泛型指定为这样:

//泛型指定为Object子类
protected void subscribeActual(final Observer<? extend Object> observer)

不料却报错了,看了下报错原因是 Observe r强迫指定泛型为 <? super T> 类型,然后我改成这样就没问题了.

protected void subscribeActual(final Observer<? super Object> observer)

我感觉很奇怪,我想发送事件的是 String 类型,是 Object 的子类,应该使用 <? extend Object> 啊,然而使用 <? super Object> 才是正确的。 思考了半天终于明白:

  • 使用 <? extend Object> 表示类型为 Object 以及子类,表示一个未知的类型,可能是 Int ,也能是 String ,编译器不知道你具体指定的类型,所以不允许你发送 String ,因为你泛型也可以指定为Int或者其它.
  • 使用 <? super Object> 表示具体类型只能为 Object 以及父类,因 为Object 没有父类,也就是只能 是Object , Strin g和 Int 都是 Object 子类,所以发送 String 或者 Int 都是没问题的, Object 类型的引用均能指向他们.

写好了之后修改RxView类的代码

public class RxView {

    /**
     * 这里肯定是返回一个可被订阅的observable
     *
     * @param view 需要被订阅点击事件的view
     * @return
     */
    public static Observable<Object> clicks(View view) {
        return new ViewClickObservale(view);
    }
    
}

不再返回 null ,返回我们刚写好的 Observable .然后进行测试.

MainActivity中的代码:

public class MainActivity extends AppCompatActivity {

    String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        RxView.clicks(findViewById(R.id.tv_obs))
                .subscribe(new Consumer<Object>() {
                    @Override
                    public void accept(Object o) throws Exception {
                        Log.d(TAG, "我被点击了");
                    }
                });
    }
}

布局中就一个 TextView ,我点击了三次,打印出的日志如下:

05-03 23:55:42.143 1770-1770/april.lesincs.rxbinding_demo D/MainActivity: 我被点击了
05-03 23:55:43.068 1770-1770/april.lesincs.rxbinding_demo D/MainActivity: 我被点击了
05-03 23:55:43.780 1770-1770/april.lesincs.rxbinding_demo D/MainActivity: 我被点击了

测试成功!一个简易版的 RxBinding 就撸出来了,代码不多,但是需要对 RxJava 有一定理解才能知道为啥要这么写,原版中还多了 取消订阅 以及 主线程检测 的功能,可以说是十分严谨了,我这里就不继续撸了,想了解的可以去看看源码.

下篇准备实现第二个目标: TextView 以及 子类 的的 文字变化 RxJava 实现 ,敬请期待.

原文  https://juejin.im/post/5aeb07cef265da0b74520315
正文到此结束
Loading...