RxJava快速入门

96
键盘男
0.1 2016.02.08 22:43* 字数 1328
photo-1429042007245-890c9e2603af.jpg

前言

最近研究很火的开源库RxJava,看过很多国内android工程师写的介绍。例如,Flipboard的扔物线同学写的《给 Android 开发者的 RxJava 详解》,讲解得非常通俗易懂。不过我觉得对于初学者,还是不够直观(可能是我比较蠢)。

本文重点将一些常用场景罗列出来,让大家简单地入门RxJava。

RxJava是什么

RxJava的github官网第一句说到:

“RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences.”

本人理解:“RxJava是一个实现java响应式编程的库,让异步事件以序列的形式组织代码。”


引入RxJava

build.gradledependencies加入

dependencies {
    ...
    compile 'io.reactivex:rxandroid:1.1.0'
    compile 'io.reactivex:rxjava:1.1.0'
}

异步网络请求:

场景:异步网络请求一个User数据,并在TextView展示。

平常代码:

TextView textView = ...;

Map<String, String> params = new HashMap<>();
params.put("user_id", userid);// 请求参数

UserHttp client = new UserHttp();
client.post("http://server.com/user", params, new CallBack<String>() { // 异步请求
     @Override
     protected void onSuccess(String result) { // 在UI线程回调
         // 返回的字符串(通常是一个json),解析成User对象
         User user = parse(result); 

          textView.setText(user.getName());
     }
});

大概就是这样子了吧,当然一般都会再封装一下。

用RxJava大概是这样子:

TextView textView = ...;

Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
    @Override
    public void call(final Subscriber<? super String> subscriber) {// 下面subscribeOn(Schedulers.newThread()) 把这方法设定在新线程回调
        Map<String, String> params = new HashMap<>();
        params.put("user_id", userid);// 请求参数

        UserHttp client   = new UserHttp();
        Response response = client.post("http://kkmike999.com/user", params);// 同步请求

        if (response.status == 200) { // 请求成功
            String result = response.getResult();
            subscriber.onNext(result);
            subscriber.onCompleted();
        } else {
            // 请求失败
            subscriber.onError(new Throwable(response.getMessage()));
        }
    }
})
.subscribeOn(Schedulers.newThread()) // 设置call(...)方法,在新线程回调;

// 可封装得更美观 Observable<String> observable = UserHttp.create(userid);

observable
          .observeOn(AndroidSchedulers.mainThread())// 让下面onNext()、onError()、onComplete()在UI线程(主线程)回调
          .subscribe(new Subscriber<String>() {
              @Override
              public void onNext(String result) { // 上面 subscriber.onNext(result)在这里回调
                  // 返回的字符串(通常是一个json),解析成User对象
                  User user = parse(result);

                  textView.setText(user.getName());
              }

              @Override
              public void onError(Throwable e) {} // 上面subscriber.onError(new Throwable(msg))在这里回调

              @Override
              public void onCompleted() {}
});

“这是什么鬼,代码量不是多了吗?”(心中千万只草泥马奔腾......)

在这么简单的场景上,使用RxJava代码量确实会变多。但是(掩饰),请求User、解析User数据的逻辑拆开,也可设定什么事件在什么线程执行。subscribeOn(Schedulers.newThread())设定下面call(subscriber<String>)在新线程回调,observeOn(AndroidSchedulers.mainThread())设定本行下面的函数onNextonErroronCompleted都在主线程执行。一行代码切换事件回调的线程,是不是很牛逼?

关于subscribeOn()observeOn()Schedulers的解释,参考《给 Android 开发者的 RxJava 详解》

异步请求&处理数据,主线程UI操作

场景条件苛刻点:User user = parse(result);是一个耗时操作,要求异步线程处理,setText()在主线程操作。这时应该怎么处理?

平常代码:

client.post("http://server.com/user", params, new CallBack<String>() { // 异步请求
     @Override
     protected void onSuccess(String result) { // 在UI线程回调
          // 异步处理
          new Thread(new Runnable(){
              @Override
              public void run(){
                  User user = parse(result); 

                  // 主线程
                  runOnUiThread(new Runnable(){
                      @Override
                      public void run(){
                          textView.setText(user.getName());
                      }
                  });
              }
          }).start();
     }
});

蛋蛋疼的感觉出来木有? 八百个回调!!!这种状况,业界称为“Callback Hell(回调地狱)”.

用RxJava让你爽爽:

Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>(){
    call(){...} // Thread1回调
})
.subscribeOn(Schedulers.newThread()) // 设置 新线程Thread1回调call(...)

observable
        .map(new Func1<String, User>() { 
            @Override
            public User call(String result) { // Thread1回调
                User user = parse(result); 
                return user;
            }
        })
        .observeOn(AndroidSchedulers.mainThread()) // 切换回主线程
        .subscribe(new Subscriber<User>() { 
            @Override
            public void onNext(User user) { // 主线程
                textView.setText(user.getName());
            }
        });

没有多重回调了!
map(new Func1<String, User>() {...}做了转换工作,把string转成User,交给Subscriber<User>处理。

RxJava流程图1.png

事件流

RxJava最大特点,就是以“事件流”(前言中“序列的形式”)写代码。

上文中 事件1 网络请求call(Subscriber<String>...)、事件2 json->User map(new Func1<String,User>...)、事件3 textView.setText() ,是事件先后顺序,自上而下写代码,而不像平常代码那样“嵌套回调”。

随着业务日益复杂,代码会变得臃肿。多数情况下工程师会把代码挪到Utils、Manager、Controller里面,即使这样,由于多线程、并发,“嵌套回调”无可避免。使用RxJava可以解决“Callback Hell”,让业务逻辑非常清晰,尽管读代码的人不知道内部怎么实现,但清楚每一个事件发生的顺序,看代码效率提高,做测试也容易。

这就是为什么要用RxJava的最大原因。

值得注意,事件流在调用subscribe()之后,才开始工作。


多线程处理

还是之前的场景:....先在Thread1把string转成JSONObject,再在Thread2将JSONObjectUser... (此处为了演示故意把情景弄复杂)

observable.subscribeOn(Schedulers.newThread()) // 新线程Thread1
        .map(new Func1<String,JSONObject>(){
            @Override
            public JSONObject call(String result) { // 在Thread1运行
                return new JSONObject(result);
            }
        })
        .subscribeOn(Schedulers.newThread()) // 切换新线程Thread2
        .map(new Func1< JSONObject, User>() { 
            @Override
            public User call(JSONObject json) { // 在Thread2运行
                User user = parse(json); // 与之前的parse()函数不一样了
                return user;
            }
        })
        .observeOn(AndroidSchedulers.mainThread()) // 切换回主线程
        .subscribe(new Subscriber<User>() { 
            @Override
            public void onNext(User user) { // 主线程
                textView.setText(user.getName());
            }
        });

好屌对吧! 还有flatMap()可以做转换,跟map()有区别,由于有点复杂,此处不列举flatMap()

RxJava流程图2.png

加入进度条

场景:....在请求开始前,显示进度条,数据处理完后,隐藏进度条...

RxJava:

    ProgressDialog progress = ...;
    progress.show();
    
    ...
    .observeOn(AndroidSchedulers.mainThread()) // 规定Subscriber在主线程回调
    .subscribe(new Subscriber<List<String>>() { // 主线程
        @Override
        public void onNext(List<String> strings) {...}

        @Override
        public void onComplete() {
          progress.dismiss();    
        }
    }

onNextonComplete被设置在主线程执行,因此可以执行ui相关代码。


变种观察者模式

RxJava说是观察者,但实际用起来有出入(《给 Android 开发者的 RxJava 详解》有解释为什么),特别是命名会让刚接触的同学混淆。所以我不强调RxJava的观察者模式,大家懂得使用后再体会。


总结

使用RxJava写代码,嵌套的逻辑、各线程回调...都会以‘序列’的形式(通常称为‘流’)表现,你不会在RxJava代码看到嵌套回调(如果有,很可能是使用不当)。‘流’让代码更直观,逻辑按事件处理顺序,一步一步来。读者非常清晰哪个事件在哪个线程执行,这一点至关重要。

刚使用门槛稍高,但随着业务复杂化,RxJava的优势日渐明显。RxJava不一定提供最好的开发模式,但接触之后,会体验到java另一层境界。

感谢

感谢撰写《给 Android 开发者的 RxJava 详解》的抛物线同学,对我学习RxJava有很大帮助。还要业界为RxJava翻译中文文档的、写各种使用文章的同学,你们辛苦了!

demo: https://github.com/kkmike999/RxJavaAsynPostDemo

版权声明:可自由转载,必须写上原文链接or原作者

java
Web note ad 1