1. 简介

RxJava

"RxJava is a Java VM implementation of Reactive Extensions: a library for composing asynchronous and event-based programs by using observable sequences",用过之后可以将其特质归纳为异步与简洁。

RxAndroid

"This module adds the minimum classes to RxJava that make writing reactive components in Android applications easy and hassle-free. More specifically, it provides a Scheduler that schedules on the main thread or any given Looper",RxJava的扩展,优雅地处理异步请求,配合Lambda表达式精简处理回调,增强程序可读性。

ButterKnife

"Field and method binding for Android views which uses annotation processing to generate boilerplate code for you",帮助我们告别findViewById,在编译阶段绑定控件对应的Java成员变量和xml标识id。

2. 依赖

Project build.grade文件

buildscript->dependencies项:

 classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1'

Module build.grade文件

apply项:

 apply plugin: 'com.jakewharton.butterknife'

dependencies项:

 compile 'io.reactivex:rxjava:1.1.5'
compile 'io.reactivex:rxandroid:1.2.0'
compile 'com.jakewharton:butterknife:8.5.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' 

一般热门开源库的更新速度是很快的,可以根据其版本间的变化来选择性地更新项目中的版本号。但是不推荐1.1.+这种写法,因为这样写之后每次构建项目都会加载库1.1.开头的最新版本。优点是不用担心最新的是1.1.1还是1.1.2,Studio会自动同步;缺点是如果项目一段时期内不需要新特性,那会因同步而浪费时间,且当版本号升为1.2.或2.0.开头时会无能为力。

3. 举例

测试项目放在Github上,基于RxJava 1.1.5、RxAndroid 1.2.0、ButterKnife 8.5.1,通过ButterKnife的注解和RxJava/RxAndroid的异步来简化原先由findViewById和Thread(runOnUiThread/Handler)或AsyncTask来实现的过程,功能主要是利用新旧两种形式来绑定控件、获取资源图片并显示在ImageView上。其实这篇文章是为了记录最近对RxJava的学习情况,Butterknife是顺带用在了项目中,接下来也会对其用法进行描述。

3.1 控件绑定

findViewById,ImageView id image_right<-->mImageRight:

 mImageRight = (ImageView) findViewById(R.id.image_right);

ButterKnife

单个,image_left<-->mImageLeft:

 @BindView(R.id.image_left)
public ImageView mImageLeft;

多个,{image_left, image_middle, image_right}<-->mImageList:

 @BindViews({R.id.image_left, R.id.image_middle, R.id.image_right})
public List<ImageView> mImageList;

findViewById绑定控件时成员变量的访问修饰符只和实际的作用范围有关,一般为声明为private,而public等也没什么问题(当然优秀的程序员要养成良好的习惯,往往将作用域限定在最小范围内)。而ButterKnife注解形式要求对应的变量一定不能是private或static修饰,为什么呢?下面会给出答案。

 ButterKnife.bind(this);

项目编译时ButterKnife工程中ButterKnifeProcessor类的process()会先执行三个操作:

a 扫描Java代码中所有的ButterKnife注解@BindView(s)、@OnClick、@OnItemClicked等;

b 当发现一个类中含有任何一个注解时,ButterKnifeProcessor会生成一个Java类,名字类似$$ViewBinder,这个新生成的类实现了ViewBinder接口;

c 这个ViewBinder类中包含了所有对应的代码,比如@BindView注解对应findViewById(),@OnClick对应setOnClickListener()等;

Activity调用ButterKnife.bind(this)后,ButterKnife会去加载对应的ViewBinder类调用它们的bind(),完成成员变量和控件标识的绑定。

可以看出,除了@BindViews部分,我们给ButterKnife的信息只有this这个参数了(定义为bind(@NonNull Activity target))。所以,绑定过程中ButterKnife会利用this来访问Activity中的变量和标识,如果变量声明成了private,就必须通过反射机制来处理,导致性能下降。目前还没有弄明白为什么不能用static修饰,希望知道的朋友能够指点。

3.2 Thread实现

 private void loadImageByThread() {
new Thread() { @Override
public void run() {
final Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_left);
MainActivity.this.runOnUiThread(new Runnable() { @Override
public void run() {
mImageLeft.setImageBitmap(bitmap);
}
});
}
}.start();
}

功能是将drawable文件夹中名为ic_left的图片显示在mImageLeft绑定的ImageView上面(布局左边,后面的例子会将不同图片显示在中间与右边,就不再解释了)。新建类Thread匿名对象开启子线程加载图片,然后调用Activity成员runOnUiThread()切换回主线程将图片显示在ImageView上。

简单起见例子中获取的图片是项目中事先准备好的资源文件,速度很快,而实际应用中一般是手机中或网络服务器上保存的图片,加载过程会比较耗时,所以才需要利用异步处理机制。

3.3 RxJava实现1

 private void loadImageByRxJava1() {
Observable.create(new Observable.OnSubscribe<Bitmap>() { @Override
public void call(Subscriber<? super Bitmap> subscriber) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_middle);
subscriber.onNext(bitmap);
subscriber.onCompleted();
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<Bitmap>() { @Override
public void onNext(Bitmap bitmap) {
mImageList.get(1).setImageBitmap(bitmap);
} @Override
public void onCompleted() {
Toast.makeText(MainActivity.this, "Complete!", Toast.LENGTH_SHORT).show();
} @Override
public void onError(Throwable e) {
Toast.makeText(MainActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
});
}

就算之前没有接触过RxJava,而对观察者模式有所了解的话,是不难看懂这段代码的。如果什么都不了解,建议先去学习观察者模式,知其所以然心里才会踏实,况且设计模式是神一样的存在,多了解总一点不会错。

代码分为三段来看就简单多了,关键在于类Observable(被观察者)和接口Observer(观察者):

第1部分2-10行,将实现了call()的接口OnSubscribe(类Observable内部)匿名对象传入create()来创建对象,泛型参数类型为Bitmap;

第2部分13-29行,将实现了onNext()、onCompleted()及onError()的接口Observer匿名对象传入subscribe()完成事件的订阅;

第3部分11-12行,从名字也可以看出,subscribeOn(Schedulers.io())指定接口OnSubscribe的call()会在线程池中以I/O操作的形式运行,observeOn(AndroidSchedulers.mainThread())指定接口Observer的三个方法在主线程中运行;

现在来看看代码是如何工作的:

当调用了subscribe()完成订阅后,call()就开始执行了。如前面所述,会在io线程中加载图片,然后调用观察者的onNext()和onCompleted(),接下来就会在主线程中更新ImageView显示内容并用Toast给出完成提示。如果图片获取过程中发生错误,则会调用onError()给出错误提示。

注意onError()和onCompleted()至多只会执行一个,且由于线程池是无限制的,大量的I/O调度操作将创建许多个线程并占用内存,需要根据实际情况在性能和便捷之间进行取舍,其实Rxjava默认的调度器是Schedulers.computation()。

3.4 RxJava实现2

 private void loadImageByRxJava2() {
Observable.from(new Integer[]{R.drawable.ic_right})
.map(new Func1<Integer, Bitmap>() { @Override
public Bitmap call(Integer idIcRight) {
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), idIcRight);
return bitmap;
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<Bitmap>() { //onNext @Override
public void call(Bitmap bitmap) {
mImageRight.setImageBitmap(bitmap);
}
}, new Action1<Throwable>() { //onError @Override
public void call(Throwable e) {
Toast.makeText(MainActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
}, new Action0() { //onComplete @Override
public void call() {
Toast.makeText(MainActivity.this, "Complete!", Toast.LENGTH_SHORT).show();
}
});
}

这是第二种形式,做的事情和3.3节是一样的(除了将图片显示在中间还是右边)。只要看两个部分代码即可:

第1部分2-10行,通过from()读入数组参数(哪怕只有一个元素也要以数组的形式,而且不能是int这样的基础类型)的同时创建了一个类Observable匿名对象,然后调用map()会将之前的数组元素一一映射到接口Func1匿名对象实现的call()中;

第2部分13-31行,将实现了call()的接口Action1和Action0匿名对象作为观察者传入subscribe()完成事件的订阅;

对比后可以发现:

a 这里被观察者的call()是有返回值的(之前没有),接收一个整型参数,返回一个Bitmap对象;

b 接口Action1的call()是有参的,Action0的call()是无参的,正好分别对应onNext(有参)、onError(有参)和onCompleted(无参),而传入的顺序也必须按照N->E->C;

那么当数组中有多个元素时,实际的订阅情况到底是什么样的?首先是一一映射,然后是异步和独立,即一个Integer参数会执行一次call(),获取并返回Bitmap后会调用一次观察者的第1个call(),N个参数就进行N轮这个过程。如果失败则调用一次第二个call(),全部成功则调用一次第三个call(),实现批处理需求是很简洁和高效的。

4. 总结

RxJava还有很多好用的类/接口和方法,建议搞Android开发的朋友有时间研究一二。最后放上结果图:

开源库RxJava、ButterKnife的更多相关文章

  1. 开源库RxJava、ButterKnife学习记录

    1. 简介 RxJava "RxJava is a Java VM implementation of Reactive Extensions: a library for composin ...

  2. 100个Github上Android开源库

    项目名称 项目简介 1. react-native 这个是 Facebook 在 React.js Conf 2015 大会上推出的基于 JavaScript 的开源框架 React Native, ...

  3. GitHub上排名前100的Android开源库介绍(来自github)

    本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍,至于排名完全是根据 GitHub 搜索 Java 语言选择 (Best Match) 得到的结果,然后过滤了 ...

  4. GitHub Top 100的Android开源库

    摘要: 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据GitHub搜索Java语言选择「Best M... 本项目主要对目前 GitH ...

  5. GitHub 上排名前 100 的 Android 开源库进行简单的介绍

    若有任何疑问可通过邮件或微博联系我 项目名称 项目简介 1. react-native 这个是 Facebook 在 React.js Conf 2015 大会上推出的基于 JavaScript 的开 ...

  6. GitHub开源库排名一百的简单介绍,值得收藏!

    GitHub Android Libraries Top 100 简介 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据 GitHub ...

  7. 我的Android进阶之旅】GitHub 上排名前 100 的 Android 开源库进行简单的介绍

    GitHub Android Libraries Top 100 简介 本文转载于:https://github.com/Freelander/Android_Data/blob/master/And ...

  8. <Android开源库 ~ 1> GitHub Android Libraries Top 100 简介

    转载自GitHub Android Libraries Top 100 简介 本项目主要对目前 GitHub 上排名前 100 的 Android 开源库进行简单的介绍, 至于排名完全是根据 GitH ...

  9. GitHub上排名前100的Android开源库介绍

    GitHub上排名前100的Android开源库介绍 文章来源: http://www.open-open.com/news/view/1587067#6734290-qzone-1-31660-bf ...

随机推荐

  1. 怎样使用ZOL一键安装器下载中关村在线的源安装包

    怎样使用ZOL一键安装器下载中关村在线的源安装包 马根峰               (广东联合电子服务股份有限公司, 广州 510300) 摘要    中关村在线近期開始推出ZOL一键安装器,用户直 ...

  2. Map和String类型之间的转换

    前提是String的格式是map或json类型的 public static void main(String[] args) { Map<String,Object> map = new ...

  3. Java全栈程序员之03:Ubuntu下安装idea

    JetBrains的产品我曾经用过很长一段时间,它们是resharper和dotcover.VS号称宇宙最强IDE,直到它遇到了resharper,我们才知道,原来vs可以更好.DotCover是一个 ...

  4. Android 里的数据储存

    数据持久化 关于数据储存,这个话题已经被反复讨论过很多次了,我是不建议把网络存储这种方式纳入到数据储存的范围的,因为这个和Android没多少关系,因此就有如下的分类: 本地储存(也称之为数据持久化, ...

  5. 手把手教你整合最优雅SSM框架:SpringMVC + Spring + MyBatis

    在写代码之前我们先了解一下这三个框架分别是干什么的? 相信大以前也看过不少这些概念,我这就用大白话来讲,如果之前有了解过可以跳过这一大段,直接看代码! SpringMVC:它用于web层,相当于con ...

  6. 海量数据拆分到nosql系统的一种方案

    获取某用户的好友最新动态. 我们大体上来说先按照用户ID将用户的好友一致性哈希到几个mongodb集群,然后把用户的最新信息也存储到mongodb中.然后利用消息系统保持数据库中的数据和mongdb中 ...

  7. Gradle 中 buildConfigField的巧妙应用

    当用AndroidStudio来进行Android项目开发时,build.gradle就是这个工具的核心部分,所有的依赖,debug/release设置,混淆等都在这里进行配置. 下面就主要来记录下利 ...

  8. 适合初学者的python实际例子

    最近在github上发现了一个有意思的项目,很适合初学者学习python代码. 学习一门语言刚开始的时候是很枯燥的,各种概念语法以及无聊的打印都会让人失去更进一步学习的动力. 很多同学在学习了一段时间 ...

  9. 关于JS拒绝访问错误

    错误如图所示,只要点击红色区域 内的任何一部分都会弹出 “js拒绝访问”,以为是浏览器的原因,卸载安装都不好使,左面的是树.点击树的节点弹出右面相应的页面. Node.NavigateUrl = “h ...

  10. Instrumentation 功能介绍(javaagent)

    利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instrument 功能从本地代码中 ...