RxAndroid是RxJava的扩展, 优雅地处理异步请求. RxAndroid配合Lambda表达式, 精简处理回调, 使程序更具有可读性. Rx作为Android最优秀的开源库之一, 极大地提高生产力, 我们需要掌握. 本文由浅入深, 介绍一些常见的使用方法, 并附有源码.
更多: http://www.wangchenlong.org/
本文代码的GitHub下载地址.
要点包含:
(1) 链式表达式的使用方式.
(2) Lambda的应用.
(3) Rx处理网络请求.
(4) 线程自动管理, 防止内存泄露.
(5) RxBinding绑定控件的异步事件.
![Rx]()
基础
当然, 从一个崭新的HelloWorld项目开始.
添加Gradle配置.
1 2 3
|
compile 'com.jakewharton:butterknife:7.0.1' compile 'io.reactivex:rxandroid:1.1.0'
|
RxAndroid是本文的核心依赖, 同时添加RxJava. 还有ButterKnife注解库.
Lambda表达式, 是写出优雅代码的关键, 参考.
1 2 3 4 5 6 7 8 9 10 11 12
|
plugins { id "me.tatarka.retrolambda" version "3.2.4" }
android { ...
compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } }
|
Gradle 2.1+以上, 配置非常简单, 添加一个plugin和一个Java1.8兼容即可.
从主MainActivity跳转至SimpleActivity.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
|
|
在SimpleActivity中, 创建一个观察者, 收到字符串的返回.
创建两个订阅者, 使用字符串输出信息.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
|
在页面中, 观察者接收信息, 发送至主线程AndroidSchedulers.mainThread(), 再传递给订阅者, 由订阅者最终处理消息. 接收信息可以是同步, 也可以是异步.
1 2 3 4 5 6 7 8 9 10 11 12 13 14
|
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_simple); ButterKnife.bind(this);
|
最基础的RxAndroid使用.
![基础]()
更多
我们已经熟悉了初步的使用方式, 在接着学习一些其他方法, 如
just: 获取输入数据, 直接分发, 更加简洁, 省略其他回调.
from: 获取输入数组, 转变单个元素分发.
map: 映射, 对输入数据进行转换, 如大写.
flatMap: 增大, 本意就是增肥, 把输入数组映射多个值, 依次分发.
reduce: 简化, 正好相反, 把多个数组的值, 组合成一个数据.
来看看这个示例, 设置两个不同类型数组, 作为输入源, 根据不同情况分发数据.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
|
这次简化调用代码, 因为有时候我们对异常并不是很关心,
只要能catch异常即可, 因此流仅仅关注真正需要的部分.
输入字符串, 变换大写, 输出至控件中显示.
just可以非常简单的获取任何数据, 分发时, 选择使用的线程.
map是对输入数据加工, 转换类型, 输入Func1, 准换大写字母.
Func1代表使用一个参数的函数, 前面是参数, 后面是返回值.
Action1代表最终动作, 因而不需要返回值, 并且一个参数.
输入数组, 单独分发数组中每一个元素, 转换大写, 输入Toast连续显示.
from是读取数组中的值, 每次单独分发, 并分发多次, 其余类似.
输入数组, 映射为单独分发, 并组合到一起, 集中显示.
这次是使用just分发数组, 则分发数据就是数组, 并不是数组中的元素.
flatMap把数组转换为单独分发, Func1内部使用from拆分数组.
reduce把单独分发数据集中到一起, 再统一分发, 使用Func2.
最终使用Action1显示获得数据. 本次代码也更加简洁.
由此我们可以观察到, Rx的写法可以是多种多样, 合理的写法会更加优雅.
效果
![效果]()
Lambda
Lambda表达式和Rx非常契合, 可以省略大量的内部类, 如Func和Action.
我们把上个示例, 用Lambda再写一次, 功能相同.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
|
|
这次没有使用常规的Lambda表达式, 而是更简单的方法引用(Method References).
方法引用: 方法参数和返回值与Lambda表达式相同时, 使用方法名代替.
网络请求
Retrofit是网络请求库, 刚推出2.0版本. Rx的一个核心应用就是处理异步网络请求, 结合Retrofit, 会更加方便和简洁. 参考.
引入库
1 2 3 4 5 6 7
|
compile 'com.android.support:recyclerview-v7:23.1.1'
|
recyclerview和picasso为了显示. retrofit系列是网络请求.
主页使用一个简单的列表视图, 展示Github的用户信息.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
|
在列表中提供点击用户信息跳转至用户详情.
NetworkWrapper.getUsersInto(adapter) 请求网络, 设置适配器信息.
关键部分, 适配器, 其中包含ViewHolder类和数据类.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72
|
|
添加数据addUser, 其中notifyItemInserted通知更新.
可以自动生成Json解析类的网站.
首先创建`Retrofit``服务, 通过服务获取数据, 再依次分发给适配器.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
|
网络请求无法在主线程上执行, 需要启动异步线程, 如Schedulers.newThread().
使用工厂模式ServiceFactory创建服务, 也可以单独创建服务.
创建Retrofit服务的工厂类.
这是Retrofit 2.0的写法, 注意需要添加Rx和Gson的解析.
设置网络请求的Url.
显示用户
![显示]()
详情页面与主页类似, 参考代码, 不做细说.
线程安全
Rx的好处之一就是可以防止内存泄露, 即根据页面生命周期, 处理异步线程的结束. 可以使用RxLifecycle库处理生命周期.
Activity类继承RxAppCompatActivity, 替换AppCompatActivity.
启动一个循环线程.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
|
|
继承RxAppCompatActivity, 添加bindToLifecycle方法管理生命周期. 当页面onPause时, 会自动结束循环线程. 如果注释这句代码, 则会导致内存泄露.
RxBinding
RxBinding是Rx中处理控件异步调用的方式, 也是由Square公司开发, Jake负责编写. 通过绑定组件, 异步获取事件, 并进行处理. 编码风格非常优雅.
除了RxJava, 再添加RxBinding的依赖.
Toolbar和Fab, 两个较新的控件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
|
<?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical" tools:context=".BindingActivity">
<android.support.design.widget.AppBarLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar android:id="@+id/rxbinding_t_toolbar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:background="?attr/colorPrimary" android:popupTheme="@style/AppTheme.PopupOverlay" tools:targetApi="21"/>
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_rxbinding"/>
<android.support.design.widget.FloatingActionButton android:id="@+id/rxbinding_fab_fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="bottom|end" android:layout_margin="@dimen/fab_margin" android:src="@android:drawable/ic_dialog_email"/>
</android.support.design.widget.CoordinatorLayout>
|
两个EditText控件, 对比传统方法和RxBinding方法.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40
|
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="@dimen/activity_margin" app:layout_behavior="@string/appbar_scrolling_view_behavior" tools:context=".BindingActivity" tools:showIn="@layout/activity_binding">
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/usual_approach"/>
<EditText android:id="@+id/rxbinding_et_usual_approach" android:layout_width="match_parent" android:layout_height="48dp" android:hint="@null"/>
<TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/reactive_approach"/>
<EditText android:id="@+id/rxbinding_et_reactive_approach" android:layout_width="match_parent" android:layout_height="48dp" android:hint="@null"/>
<TextView android:id="@+id/rxbinding_tv_show" android:layout_width="match_parent" android:layout_height="wrap_content"/>
</LinearLayout>
|
使用ButterKnife注入控件, 使用RxBinding方式绑定控件, 异步监听事件.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
|
/** * Rx绑定 * <p> * Created by wangchenlong on 16/1/25. */ public class BindingActivity extends AppCompatActivity {
@Bind(R.id.rxbinding_t_toolbar) Toolbar mTToolbar; @Bind(R.id.rxbinding_et_usual_approach) EditText mEtUsualApproach; @Bind(R.id.rxbinding_et_reactive_approach) EditText mEtReactiveApproach; @Bind(R.id.rxbinding_tv_show) TextView mTvShow; @Bind(R.id.rxbinding_fab_fab) FloatingActionButton mFabFab;
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_binding); ButterKnife.bind(this);
initToolbar(); // 初始化Toolbar initFabButton(); // 初始化Fab按钮 initEditText(); // 初始化编辑文本 }
// 初始化Toolbar private void initToolbar() { // 添加菜单按钮 setSupportActionBar(mTToolbar); ActionBar actionBar = getSupportActionBar(); // 添加浏览按钮 if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(true); }
RxToolbar.itemClicks(mTToolbar).subscribe(this::onToolbarItemClicked);
RxToolbar.navigationClicks(mTToolbar).subscribe(this::onToolbarNavigationClicked); }
// 点击Toolbar的项 private void onToolbarItemClicked(MenuItem menuItem) { String m = "点击\"" + menuItem.getTitle() + "\""; Toast.makeText(this, m, Toast.LENGTH_SHORT).show(); }
// 浏览点击 private void onToolbarNavigationClicked(Void v) { Toast.makeText(this, "浏览点击", Toast.LENGTH_SHORT).show(); }
@Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_rxbinding, menu); return super.onCreateOptionsMenu(menu); }
// 初始化Fab按钮 private void initFabButton() { RxView.clicks(mFabFab).subscribe(this::onFabClicked); }
// 点击Fab按钮 private void onFabClicked(Void v) { Snackbar snackbar = Snackbar.make(findViewById(android.R.id.content), "点击Snackbar", Snackbar.LENGTH_SHORT); snackbar.show(); RxSnackbar.dismisses(snackbar).subscribe(this::onSnackbarDismissed); }
// 销毁Snackbar, event参考{Snackbar} private void onSnackbarDismissed(int event) { String text = "Snackbar消失代码:" + event; Toast.makeText(this, text, Toast.LENGTH_SHORT).show(); }
// 初始化编辑文本 private void initEditText() { // 正常方式 mEtUsualApproach.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override public void onTextChanged(CharSequence s, int start, int before, int count) { mTvShow.setText(s); }
@Override public void afterTextChanged(Editable s) {
}
});
// Rx方式 RxTextView.textChanges(mEtReactiveApproach).subscribe(mTvShow::setText); } }
|
Toolbar使用RxToolbar监听点击事件; Snackbar使用RxSnackbar监听;
EditText使用RxTextView监听; 其余使用RxView监听.
![动画]()
OK, That’s all. Enjoy it!
原始地址:
http://www.wangchenlong.org/2016/03/20/1603/207-rxjava-first/
欢迎Follow我的GitHub, 关注我的简书, 微博, CSDN, 掘金, Slides.
我已委托“维权骑士”为我的文章进行维权行动. 未经授权, 禁止转载, 授权或合作请留言.