热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

开发笔记:RxJava入门系列四,Android中的响应式编程

篇首语:本文由编程笔记#小编为大家整理,主要介绍了RxJava入门系列四,Android中的响应式编程相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了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 cb);

但是使用RxJava,你可以通过返回Observable对象来代替传入Callback对象:

@GET("/user/{id}/photo")
Observable 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 newMethod() {
return Observable.just(oldMethod());
}

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

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

private Object slowBlockingMethod() { ... }
public Observable 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 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





推荐阅读
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文讨论了在手机移动端如何使用HTML5和JavaScript实现视频上传并压缩视频质量,或者降低手机摄像头拍摄质量的问题。作者指出HTML5和JavaScript无法直接压缩视频,只能通过将视频传送到服务器端由后端进行压缩。对于控制相机拍摄质量,只有使用JAVA编写Android客户端才能实现压缩。此外,作者还解释了在交作业时使用zip格式压缩包导致CSS文件和图片音乐丢失的原因,并提供了解决方法。最后,作者还介绍了一个用于处理图片的类,可以实现图片剪裁处理和生成缩略图的功能。 ... [详细]
  • 如何查询zone下的表的信息
    本文介绍了如何通过TcaplusDB知识库查询zone下的表的信息。包括请求地址、GET请求参数说明、返回参数说明等内容。通过curl方法发起请求,并提供了请求示例。 ... [详细]
  • 本文介绍了OpenStack的逻辑概念以及其构成简介,包括了软件开源项目、基础设施资源管理平台、三大核心组件等内容。同时还介绍了Horizon(UI模块)等相关信息。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 闭包一直是Java社区中争论不断的话题,很多语言都支持闭包这个语言特性,闭包定义了一个依赖于外部环境的自由变量的函数,这个函数能够访问外部环境的变量。本文以JavaScript的一个闭包为例,介绍了闭包的定义和特性。 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 基于Axis、XFire、CXF的webservice客户端调用示例
    本文介绍了如何使用Axis、XFire、CXF等工具来实现webservice客户端的调用,以及提供了使用Java代码进行调用的示例。示例代码中设置了服务接口类、地址,并调用了sayHello方法。 ... [详细]
author-avatar
别想着摆脱_525
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有