在前三部分,我在通用层面介绍了RxJava的工作原理。但是作为一个Android开发者,如何在工作中使用它呢?下面是一些给Android开发者的RxJava的具体应用。

RxAndroid

RxAndroid是RxJava在Android开发中的拓展。它包含能节省我们大量时间的特殊bindings。

首先,其中有AndroidSchedulers,它能提供专门为Android线程系统提供的schedulers。需要在UI线程运行代码?没问题——只需要使用AndroidSchedulers.mainThread()方法即可:

retrofitService.getImage(url)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

如果你拿到的是Handler,可以通过HandlerThreadScheduler创建一个scheduler绑定在Handler上。

接下来介绍的是AndroidObservable,它能提供很多在Android生命周期中的特色功能。bindActivity()bindFragment()方法能停止发出items,在ActivityFragment结束的时候。另外会自动为订阅使用AndroidSchedulers.mainThread()。(因此你不需要在Activity或Fragment无效的时候来改变状态)。

AndroidObservable.bindActivity(this, retrofitService.getImage(url))
.subscribeOn(Schedulers.io())
.subscribe(bitmap -> myImageView.setImageBitmap(bitmap));

我也喜欢AndroidObservable.fromBroadcast(),它让我们可以创建一个像BroadcastReceiver那样工作的Observable。以下是无论什么时候网络连接发生变化时通知的方法。

IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);
AndroidObservable.fromBroadcast(context, filter)
.subscribe(intent -> handleConnectivityChange(intent));

最后介绍的是ViewObservable,它能为Views添加bindings。如果你想要得到View每次被点击的事件,可以通过ViewObservable.clicks()方法。也可以通过ViewObservable.text()方法来监测TextView的内容发生的任何变化。

ViewObservable.clicks(mCardNameEditText, false)
.subscribe(view -> handleClick(view));

Retrofit

有一个知名并且支持RxJava的库:Retrofit,它是Android开发中的非常出名的REST风格的网络库。通常,我们定义一个异步方法并添加Callback:

@GET("/user/{id}/photo")
void getUserPhoto(@Path("id") int id, Callback<Photo> cb);

用了RxJava,可以用Observable代替Callback作为返回值。

@GET("/user/{id}/photo")
Observable<Photo> getUserPhoto(@Path("id") int id);

现在可以对Observable做你想要的操作了,不仅可以获得数据,也能变换它。

Retrofit支持Observable,也使得合并多个REST请求变得容易。例如,假设我们有一个请求获得图片,另一个请求获得元数据(metadata)。我们可以把结果组合到一起:

Observable.zip(
service.getUserPhoto(id),
service.getPhotoMetadata(id),
(photo, metadata) -> createPhotoWithData(photo, metadata))
.subscribe(photoWithData -> showPhoto(photoWithData));

我在第二部分展示了相似的例子(使用flatMap())。这便证明使用RxJava+Retrofit合并多个REST请求有多简单。

旧的,耗时长的代码

Retrofit能返回Observerables这固然非常好,但是如果你用的其他的库不支持它呢?或者你想要将一些内部代码转换为Observables?总之,你如何将旧的代码和新的代码联系在一起,而不用重写所有代码?

Observable.just()Observable.from()大多数时候足以将以前的代码转换为Observable

private Object oldMethod() { ... }

public Observable<Object> newMethod() {
return Observable.just(oldMethod());
}

这在oldMethod()耗时少的情况下能正常工作,但是如果耗时长呢?因为调用oldMethod(),在它被传递到Observable.just()方法前就会阻塞了线程。

为了对付这个问题,以下是我一直使用的方法——使用defer方法将耗时长的部分包装起来。

private Object slowBlockingMethod() { ... }

public Observable<Object> newMethod() {
return Observable.defer(() -> Observable.just(slowBlockingMethod()));
}

现在, 直到你订阅Observable才会去调用slowBlockMethod()方法。

生命周期

我把最难的部分留在了最后。你是如何处理RxJava与Activity的生命周期的(配合使用的)?以下两个问题会多次出现:

  • 1.Activity的配置发生变化后继续订阅一个Subscribtion

    假设你用Retrofit做了一次REST请求,想要把请求结果展示在ListView上。如果用户旋转了屏幕怎么办?如果你想要继续相同的请求,但是如何做呢?

  • 2.Observables持有Context引用会造成内存泄漏。

    这个问题是由于创建了一个以某种方式持有Contextsubscribtion,特别是你和Views交互的时候容易出现。如果Observable没有准时完成,最后可能持有非常多的额外内存。

不幸的是,两个问题都没有很好的解决办法。但是有一些能节约你时间的指导方针。

第一个问题能用一些RxJava内置的缓存机制解决,因此你可以取消订阅/再订阅同一个Observable,而不需要重复它之前的(准备)工作。特别的,cache()(或是replay())方法将继续在(方法)之下的请求(即使你取消订阅)。这意味着Activity重新生成时,你能重新开始新的subscription。

Observable<Photo> request = service.getUserPhoto(id).cache();
Subscription sub = request.subscribe(photo -> handleUserPhoto(photo)); // ...When the Activity is being recreated...
sub.unsubscribe(); // ...Once the Activity is recreated...
request.subscribe(photo -> handleUserPhoto(photo));

注意我们在两种情况下,用的是同一个缓存的请求(request),那种方式隐含的调用只会发生一次。存放请求的地方你自己决定,但是像所有的生命周期解决方案一样,它必须存放在生命周期之外的地方(一个保存的fragment,单例等等)。

第二问题可以通过根据生命周期正确的取消订阅来实现。通用的方法是用一个CompositeSubscription来持有所有的Subscription,然后在onDestroy()onDestroyView()方法中取消所有的订阅。

private CompositeSubscription mCompositeSubscription
= new CompositeSubscription(); private void doSomething() {
mCompositeSubscription.add(
AndroidObservable.bindActivity(this, Observable.just("Hello, World!"))
.subscribe(s -> System.out.println(s)));
} @Override
protected void onDestroy() {
super.onDestroy(); mCompositeSubscription.unsubscribe();
}

更进一步,你可以创建一个根ActivityFragment,顺带添加一个CompositeSubscription,随后可以相应的取消订阅。

注意,只要你调用了CompositeSubscription.unsubscribe(),该对象(`CompositeSubscription`)就不能用了。因为它会自动取消订阅随后你添加的任何事物。如果你今后想要使用这种方法,你必须创建一个新的CompositeSubscription作为替代。

两个问题的解决方法都涉及到添加代码,我希望有一天能出现不需要写这些样板代码就能解决这些问题的天才。

本文翻译自Grokking RxJava, Part 4: Reactive Android,著作权归原作者danlew所有。译文由JohnTsai翻译。转载请注明出处,并保留此段声明。

理解RxJava:(四)Reactive Android的更多相关文章

  1. 理解RxJava:(三)RxJava的优点

    理解RxJava:(三)RxJava的优点 在第一部分,讲解了RxJava的基本结构.在第二部分,展示了operators的强大之处.但是你们可能仍然没有被说服,也没有足够的理由信服.下面是一些能让你 ...

  2. 理解RxJava:(一)基础知识

    理解RxJava:(一)基础知识 本文翻译自Grokking RxJava, Part 1: The Basics,著作权归原作者danlew所有.译文由JohnTsai翻译.转载请注明出处,并保留此 ...

  3. 【原创】NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战

    概述 本文演示的是一个Android客户端程序,通过UDP协议与两个典型的NIO框架服务端,实现跨平台双向通信的完整Demo. 当前由于NIO框架的流行,使得开发大并发.高性能的互联网服务端成为可能. ...

  4. 四、Android学习第四天——JAVA基础回顾(转)

    (转自:http://wenku.baidu.com/view/af39b3164431b90d6c85c72f.html) 四.Android学习第四天——JAVA基础回顾 这才学习Android的 ...

  5. 20162330 实验四 《Android程序设计》 实验报告

    2016-2017-2 实验报告目录: 1 2 3 4 5 20162330 实验四 <Android程序设计> 实验报告 课程名称:<程序设计与数据结构> 学生班级:1623 ...

  6. 理解 RxJava 的线程模型

    来源:鸟窝, colobu.com/2016/07/25/understanding-rxjava-thread-model/ 如有好文章投稿,请点击 → 这里了解详情 ReactiveX是React ...

  7. 20145207《Java程序设计》实验四( Android程序设计)实验报告

    <Java 程序设计>实验四( Android程序设计)实验报告 目录 改变 Android开发基础实验要求 实验成果 课后思考 改变 修改了之前仅仅是贴了图片,连代码都没粘的状态.增加了 ...

  8. 实验四《Android程序设计》实验报告封面

    实验四<Android程序设计>实验报告封面 课程:Java程序设计 班级:1753班 姓名:许钰玮 学号:20175329 指导教师:娄嘉鹏 实验日期:2019年5月13日 实验时间:1 ...

  9. 20145206实验四《Android开发基础》

    20145206 实验四<Android开发基础> 实验内容 ·安装Android Studio ·运行安卓AVD模拟器 ·使用安卓运行出虚拟手机并显示HelloWorld以及自己的学号 ...

随机推荐

  1. atitit.api设计 方法 指南 手册 v2 q929.docx

    atitit.api设计 方法 指南 手册 v2 q929.docx atitit.api设计原则与方法 1. 归一化(锤子钉子理论)1 1.1. 链式方法2 1.2. 规则5:建立返回值类型2 1. ...

  2. paip.性能跟踪profile原理与架构与本质-- python扫带java php

    paip.性能跟踪profile原理与架构与本质-- python扫带java php ##背景 弄个个输入法音标转换atiEnPH工具,老是python性能不的上K,7k记录浏览过k要30分钟了. ...

  3. 用非管理员权限启动主程序,并用管理员权限启动子程序,导致WM_COPYDATA消息发送失败的问题

    问题描述 :     用非管理员权限启动dzh,dzh再启动dtssm,由于dtssm的配置文件app.manifest 中设置了requireAdministrator,导致dtssm总是以管理员权 ...

  4. Visual Studio 2013 无法正常打开项目文件

    提示:无法打开 vcxproj 因为此版本的应用程序不支持其项目类型 ,若要打开它 请使用支持此类型项目的版本. 检查  AppData\Roaming\Microsoft\VisualStudio\ ...

  5. ios app 实现热更新(无需发新版本实现app添加新功能)

    目前能够实现热更新的方法,总结起来有以下三种 1. 使用FaceBook 的开源框架 reactive native,使用js写原生的iOS应用 ios app可以在运行时从服务器拉取最新的js文件到 ...

  6. 在cxf中使用配置避免增加字段导致客户端必须更新、同步实体属性的问题

    在使用cxf实现webservice时,经常碰到的问题就是如果在服务端,修改了一个接口的签名实现,如增加一个字段,或者删除一个字段.在这种情况下,在默认的配置中,就会报以下的错误信息: org.apa ...

  7. 专访高磊:安卓APK安全加固的引领者

    高磊,爱加密CEO,安卓巴士版主之一,曾编写河南省某地市交通信息化规划十二五规划,以及参与省厅级资源共享平台设计等.之前的工作经理积累了丰富的设计规划经验,此外还具有J2EE和 Android开发经验 ...

  8. Java的自动装箱和拆箱的简单讲解

     装箱就是把基础类型封装成一个类.比如把int封装成Integer,这时你就不能把他当成一个数了,而是一个类了,对他的操作就需要用它的方法了. 拆箱就是把类转换成基础类型.比如你算个加法什么的是不能用 ...

  9. [算法导论]拓扑排序 @ Python

    class Graph: def __init__(self): self.V = [] class Vertex: def __init__(self, x): self.key = x self. ...

  10. SQL Server死锁

    SQL Server死锁 多个事务之间互相等待对方的资源,导致这些事务永久等待 注意是永久等待,而非长事务 死锁的4个条件 互斥条件(Mutual exclusion):资源不能被共享,只能由一个进程 ...