RxJava快速入门

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原作者

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,198评论 4 359
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,663评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 106,985评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,673评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 51,994评论 3 285
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,399评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,717评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,407评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,112评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,371评论 2 241
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,891评论 1 256
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,255评论 2 250
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,881评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,010评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,764评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,412评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,299评论 2 260

推荐阅读更多精彩内容