RxJava入门系列四,Android中的响应式编程

RxJava入门系列四,Android中的响应式编程

在入门系列1,2,3中,我基本介绍了RxJava是如何使用的.但是作为一名Android开发人员,你怎么让RxJava能为你所用呢?这篇博客我将针对Android开发来介绍一下RxJava的使用场景.


RxAndroid

RxAndroid是为Android打造的RxJava扩展.通过RxAndroid可以让你的Android开发变得更轻松.

首先,RxAndroid中提供了AndroidSchedulers,你可以用它来切换Android线程.你想要将代码运行在UI线程?没问题,使用AndroidSchedulers.mainThread()即可:

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

如果你已经创建了一个Handler,你还可以使用HandlerThreadScheduler创建一个调度器关联到你的Handler上.

接下来,RxAndroid中提供了AndroidObservable,它提供很多可以配合Android应用生命周期一起使用的功能.例如bindActivity()和bindFragment()两个方法,默认使用AndroidSchedulers.mainThread()线程来执行观察者代码,同时当Activity或者Fragment退出时,Observable自动停止发送新事件.

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

我同样也很喜欢其中的AndroidObservable.fromBroadcast()方法,它可以帮助你创建一个和BroadcastReceiver配合使用的Observable对象.这里展示如下如何使用Observable来监听网络状态变化:

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

最后,再介绍一下ViewObservable对象,它可以用来绑定View.例如,可以使用ViewObservable.clicks()方法来监听View的点击事件,使用ViewObservable.text()方法来监听TextView的内容变化事件.

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

Retrofit

有一个非常出名的支持RxJava的项目:Retrofit,它是一个为Android设计的简化HTTP请求的库.通常情况下,你可以添加一个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的支持使得Retrofit可以很简单的将多个REST请求合并起来.例如,我们有一个请求获取照片,还有一个请求是获取元数据.我们通过Observable将两个请求合并起来:

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

在RxJava入门系统二中,我使用flatMap做过类似的例子.这里我是想展示通过Retrofit可以多么轻松的将多个Retrofit请求合并起来.


历史代码

虽然Retrofit支持RxJava,但是你项目中使用的其他库不支持RxJava怎么办?你想把一些历史遗留代码转成RxJava的形式该怎么做?能不能通过不修改代码的方式就将历史代码转成RxJava的形式?

Observable.just()和Observable.from()一般来说可以帮助你解决这些问题:

private Object oldMethod() { ... }

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

如果oldMethod()方法的执行速度很快,这样做通常不会出什么问题.但是如果oldMethod()执行很慢呢?当你将oldMethod()传入Observable.just()方法时,由于oldMethod()执行太慢可能会导致主线程的阻塞.

为了避免这种问题,我经常使用defer()来包装运行缓慢的代码:

private Object slowBlockingMethod() { ... }

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

现在,newMethod()的调用就不会阻塞了,除非你订阅了返回的Observable对象.


生命周期

我把最难的部分留到了最后.使用RxJava如何处理Activity的生命周期?这里主要会遇到两个难题:

  1. 如何在环境配置发生变化时(例如屏幕旋转)依然保持订阅关系?

    假设你想通过Retrofit发送网络请求,并将结果显示到ListView时.如果过程中用户旋转屏幕了,你是否还想继续保持这种操作,如何保持?

  2. 如果解决Observable持有Context对象可能导致的内存泄露?

    这个问题是由于创建Subscription时保持了Context对象,这个当遇到操控View时很容易被触发.如果Observable没有及时结束,你可能会因此浪费越来越多的内存.

不幸的是,目前没有明确的方法来解决上述两个问题,但是你可以依据下面的指导来尽量规避Android中使用RxJava会遇到的坑.

第一个问题可以使用RxJava内置的缓存机制来解决,你可以对同一个Observable对象执行unsubscribe和resubscribe.Cache机制会继续执行之前的请求,即使你已经取消订阅了.这意味着你可以在Activity重建时再次建立订阅关系:

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));

第二个问题可以通过在Activity的具体生命周期函数中解除订阅关系来解决.一种很常见的模式就是使用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();
}

你还是可以实现的更通用一点,你可以在Activity/Fragment基类里创建一个CompositeSubscription对象,在基类的onDestory等相关生命周期函数中使用unsubscribe方法.

注意!一旦你调用了CompositeSubscription.unsubscribe()方法,这个CompositeSubscription对象就不可用了,如果你还想使用CompositeSubscription,就必须重新创建一个新的对象了.

解决上面的两个问题都需要新增部分代码.我希望以后能有开发者告诉我一个更好的不需要新增这么多模板代码的解决方案.


总结

RxAndroid还没有完全的适配好Android平台,毕竟RxJava都是一个很新的项目,RxAndroid就更是需要一段时间的完善了.RxAndroid还处于开发阶段,目前也没有太好的示例可供展示.我打赌一年之后,这篇博客中提到的几点建议可能就不再适用了.

同时,我发现RxJava不仅会简化你的代码逻辑,还会让你的代码变得有趣,变得优雅.


原文链接

Grokking RxJava, Part 4: Reactive Android

时间: 2024-12-18 01:02:02

RxJava入门系列四,Android中的响应式编程的相关文章

深入浅出RxJava四-在Android中使用响应式编程

原文链接 在第1,2,3篇中,我大概介绍了RxJava是怎么使用的.下面我会介绍如何在Android中使用RxJava. RxAndroid RxAndroid是RxJava的一个针对Android平台的扩展.它包含了一些能够简化Android开发的工具. 首先,AndroidSchedulers提供了针对Android的线程系统的调度器.需要在UI线程中运行某些代码?很简单,只需要使用AndroidSchedulers.mainThread(): myImageView.setImageBit

Spring Boot (十四): 响应式编程以及 Spring Boot Webflux 快速入门

1. 什么是响应式编程 在计算机中,响应式编程或反应式编程(英语:Reactive programming)是一种面向数据流和变化传播的编程范式.这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播. 例如,在命令式编程环境中,a=b+c 表示将表达式的结果赋给 a,而之后改变 b 或 c 的值不会影响 a .但在响应式编程中,a 的值会随着 b 或 c 的更新而更新. 响应式编程是基于异步和事件驱动的非阻塞程序,只需要在程序内启动少量线程扩

Unity基于响应式编程(Reactive programming)入门

系列目录 [Unity3D基础]让物体动起来①--基于UGUI的鼠标点击移动 [Unity3D基础]让物体动起来②--UGUI鼠标点击逐帧移动 时光煮雨 Unity3D让物体动起来③—UGUI DoTween&Unity Native2D实现 时光煮雨 Unity3D实现2D人物动画① UGUI&Native2D序列帧动画 时光煮雨 Unity3D实现2D人物动画② Unity2D 动画系统&资源效率 背景 前有慕容小匹夫的一篇<解构C#游戏框架uFrame兼谈游戏架构设计&

响应式编程,是明智的选择

相信你们在学习响应式编程这个新技术的时候都会充满了好奇,特别是它的一些变体,例如:Rx系列.Bacon.js.RAC等等…… 在缺乏优秀资料的前提下,响应式编程的学习过程将满是荆棘.起初,我试图寻找一些教程,却只找到少量的实践指南,而且它们讲的都非常浅显,从来没人接受围绕响应式编程建立一个完整知识体系的挑战.此外,官方文档通常也不能很好地帮助你理解某些函数,因为它们通常看起来很绕,不信请看这里: Rx.Observable.prototype.flatMapLatest(selector, [t

响应式编程(Reactive Programming)(Rx)介绍

很明显你是有兴趣学习这种被称作响应式编程的新技术才来看这篇文章的. 学习响应式编程是很困难的一个过程,特别是在缺乏优秀资料的前提下.刚开始学习时,我试过去找一些教程,并找到了为数不多的实用教程,但是它们都流于表面,从没有围绕响应式编程构建起一个完整的知识体系.库的文档往往也无法帮助你去了解它的函数.不信的话可以看一下这个: 通过合并元素的指针,将每一个可观察的元素序列放射到一个新的可观察的序列中,然后将多个可观察的序列中的一个转换成一个只从最近的可观察序列中产生值得可观察的序列. 天啊. 我看过

理解响应式编程

响应式的由来 我们先来聊一聊响应式的由来,对于它的由来,我们可能需要先从一段常见的代码片段看起 int a=1; int b=a+1; System.out.print("b="+b) // b=2 a=10; System.out.print("b="+b) // b=2 上面是一段很常见的代码,简单的赋值打印语句,但是这种代码有一个缺陷,那就是如果我们想表达的并不是一个赋值动作,而是b和a之间的关系,即无论a如何变化,b永远比a大1.那么可以想见,我们就需要花额

[译] Swift 的响应式编程

原文  https://github.com/bboyfeiyu/iOS-tech-frontier/blob/master/issue-3/Swift的响应式编程.md 原文链接 : Reactive Swift 原文作者 : Agnes Vasarhelyi 译文出自 : 开发技术前线 www.devtf.cn 译者 :Mr.Simple 校对者:Lollypo 状态 : 完成 让我们首先回到Apple刚推出Objective-C的继任者-Swift的时候,那真是一个非比寻常的时刻. Sir

RxJava入门系列三,响应式编程

RxJava入门系列三,响应式编程 在RxJava入门系列一,我向你介绍了RxJava的基础架构.RxJava入门系列二,我向你展示了RxJava提供的多种牛逼操作符.但是你可能仍然没能劝服自己使用RxJava,这一篇博客里我将向你展示RxJava提供的其他优势,没准了解了这些优势,你就真的想去使用RxJava了. 异常处理 直到目前为止,我都没有去介绍onComplete()和onError()方法.这两个方法是用来停止Observable继续发出事件并告知观察者为什么停止(是正常的停止还是因

[转]C# 互操作性入门系列(四):在C# 中调用COM组件

传送门 C#互操作系列文章: C#互操作性入门系列(一):C#中互操作性介绍 C#互操作性入门系列(二):使用平台调用调用Win32 函数 C# 互操作性入门系列(三):平台调用中的数据封送处理 C#互操作性入门系列(四):在C# 中调用COM组件 本专题概要: 引言 如何在C#中调用COM组件--访问Office 互操作对象 在C# 中调用COM组件的实现原理剖析 错误处理 小结 一.引言 COM(Component Object Modele,组件对象模型)是微软以前推崇的一个开发技术,所以