RxAndroid是RxJava的扩展, 优雅地处理异步请求. RxAndroid配合Lambda表达式, 精简处理回调, 使程序更具有可读性. Rx作为Android最优秀的开源库之一, 极大地提高生产力, 我们需要掌握. 本文由浅入深, 介绍一些常见的使用方法, 并附有源码.
本文代码的GitHub下载地址.
要点包含:
(1) 链式表达式的使用方式.
(2) Lambda的应用.
(3) Rx处理网络请求.
(4) 线程自动管理, 防止内存泄露.
(5) RxBinding绑定控件的异步事件.
基础
当然, 从一个崭新的HelloWorld项目开始.
添加Gradle配置.
1
2
3
|
compile
'com.jakewharton:butterknife:7.0.1'
compile
'io.reactivex:rxandroid:1.1.0'
// RxAndroid
compile
'io.reactivex:rxjava:1.1.0'
// 推荐同时加载RxJava
|
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
|
/**
* 主Activity, 用于跳转各个模块.
*
* @author wangchenlong
*/
public
class
MainActivity
extends
AppCompatActivity
{
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_main
)
;
}
// 跳转简单的页面
public
void
gotoSimpleModule
(
View
view
)
{
startActivity
(
new
Intent
(
this
,
SimpleActivity
.
class
)
)
;
}
// 跳转复杂的页面
public
void
gotoMoreModule
(
View
view
)
{
startActivity
(
new
Intent
(
this
,
MoreActivity
.
class
)
)
;
}
// 跳转Lambda的页面
public
void
gotoLambdaModule
(
View
view
)
{
startActivity
(
new
Intent
(
this
,
LambdaActivity
.
class
)
)
;
}
// 跳转网络的页面
public
void
gotoNetworkModule
(
View
view
)
{
startActivity
(
new
Intent
(
this
,
NetworkActivity
.
class
)
)
;
}
// 跳转线程安全的页面
public
void
gotoSafeModule
(
View
view
)
{
startActivity
(
new
Intent
(
this
,
SafeActivity
.
class
)
)
;
}
}
|
在SimpleActivity
中, 创建一个观察者, 收到字符串的返回.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 观察事件发生
Observable
.
OnSubscribe
mObservableAction
=
new
Observable
.
OnSubscribe
<String>
(
)
{
@Override
public
void
call
(
Subscriber
<
?
super
String
>
subscriber
)
{
subscriber
.
onNext
(
sayMyName
(
)
)
;
// 发送事件
subscriber
.
onCompleted
(
)
;
// 完成事件
}
}
;
.
.
.
// 创建字符串
private
String
sayMyName
(
)
{
return
"Hello, I am your friend, Spike!"
;
}
|
创建两个订阅者, 使用字符串输出信息.
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
|
// 订阅者, 接收字符串, 修改控件
Subscriber
<String>
mTextSubscriber
=
new
Subscriber
<String>
(
)
{
@Override
public
void
onCompleted
(
)
{
}
@Override
public
void
onError
(
Throwable
e
)
{
}
@Override
public
void
onNext
(
String
s
)
{
mTvText
.
setText
(
s
)
;
// 设置文字
}
}
;
// 订阅者, 接收字符串, 提示信息
Subscriber
<String>
mToastSubscriber
=
new
Subscriber
<String>
(
)
{
@Override
public
void
onCompleted
(
)
{
}
@Override
public
void
onError
(
Throwable
e
)
{
}
@Override
public
void
onNext
(
String
s
)
{
Toast
.
makeText
(
SimpleActivity
.
this
,
s
,
Toast
.
LENGTH_SHORT
)
.
show
(
)
;
}
}
;
|
在页面中, 观察者接收信息, 发送至主线程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
)
;
// 注册观察活动
@SuppressWarnings
(
"unchecked"
)
Observable
<String>
observable
=
Observable
.
create
(
mObservableAction
)
;
// 分发订阅信息
observable
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
;
observable
.
subscribe
(
mTextSubscriber
)
;
observable
.
subscribe
(
mToastSubscriber
)
;
}
|
更多
我们已经熟悉了初步的使用方式, 在接着学习一些其他方法, 如
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
|
/**
* 更多的RxAndroid的使用方法.
* <p>
* Created by wangchenlong on 15/12/30.
*/
public
class
MoreActivity
extends
Activity
{
@Bind
(
R
.
id
.
simple_tv_text
)
TextView
mTvText
;
final
String
[
]
mManyWords
=
{
"Hello"
,
"I"
,
"am"
,
"your"
,
"friend"
,
"Spike"
}
;
final
List
<String>
mManyWordList
=
Arrays
.
asList
(
mManyWords
)
;
// Action类似订阅者, 设置TextView
private
Action1
<String>
mTextViewAction
=
new
Action1
<String>
(
)
{
@Override
public
void
call
(
String
s
)
{
mTvText
.
setText
(
s
)
;
}
}
;
// Action设置Toast
private
Action1
<String>
mToastAction
=
new
Action1
<String>
(
)
{
@Override
public
void
call
(
String
s
)
{
Toast
.
makeText
(
MoreActivity
.
this
,
s
,
Toast
.
LENGTH_SHORT
)
.
show
(
)
;
}
}
;
// 设置映射函数
private
Func1
<
List
<String>
,
Observable
<String>
>
mOneLetterFunc
=
new
Func1
<
List
<String>
,
Observable
<String>
>
(
)
{
@Override
public
Observable
<String>
call
(
List
<String>
strings
)
{
return
Observable
.
from
(
strings
)
;
// 映射字符串
}
}
;
// 设置大写字母
private
Func1
<
String
,
String
>
mUpperLetterFunc
=
new
Func1
<
String
,
String
>
(
)
{
@Override
public
String
call
(
String
s
)
{
return
s
.
toUpperCase
(
)
;
// 大小字母
}
}
;
// 连接字符串
private
Func2
<
String
,
String
,
String
>
mMergeStringFunc
=
new
Func2
<
String
,
String
,
String
>
(
)
{
@Override
public
String
call
(
String
s
,
String
s2
)
{
return
String
.
format
(
"%s %s"
,
s
,
s2
)
;
// 空格连接字符串
}
}
;
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_simple
)
;
ButterKnife
.
bind
(
this
)
;
// 添加字符串, 省略Action的其他方法, 只使用一个onNext.
Observable
<String>
obShow
=
Observable
.
just
(
sayMyName
(
)
)
;
// 先映射, 再设置TextView
obShow
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
map
(
mUpperLetterFunc
)
.
subscribe
(
mTextViewAction
)
;
// 单独显示数组中的每个元素
Observable
<String>
obMap
=
Observable
.
from
(
mManyWords
)
;
// 映射之后分发
obMap
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
map
(
mUpperLetterFunc
)
.
subscribe
(
mToastAction
)
;
// 优化过的代码, 直接获取数组, 再分发, 再合并, 再显示toast, Toast顺次执行.
Observable
.
just
(
mManyWordList
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
flatMap
(
mOneLetterFunc
)
.
reduce
(
mMergeStringFunc
)
.
subscribe
(
mToastAction
)
;
}
// 创建字符串
private
String
sayMyName
(
)
{
return
"Hello, I am your friend, Spike!"
;
}
}
|
这次简化调用代码, 因为有时候我们对异常并不是很关心,
只要能catch
异常即可, 因此流仅仅关注真正需要的部分.
输入字符串, 变换大写, 输出至控件中显示.
1
2
3
4
5
6
|
// 添加字符串, 省略Action的其他方法, 只使用一个onNext.
Observable
<String>
obShow
=
Observable
.
just
(
sayMyName
(
)
)
;
// 先映射, 再设置TextView
obShow
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
map
(
mUpperLetterFunc
)
.
subscribe
(
mTextViewAction
)
;
|
just
可以非常简单的获取任何数据, 分发时, 选择使用的线程.
map
是对输入数据加工, 转换类型, 输入Func1
, 准换大写字母.
Func1
代表使用一个参数的函数, 前面是参数, 后面是返回值.
Action1
代表最终动作, 因而不需要返回值, 并且一个参数.
输入数组, 单独分发数组中每一个元素, 转换大写, 输入Toast连续显示.
1
2
3
4
5
6
|
// 单独显示数组中的每个元素
Observable
<String>
obMap
=
Observable
.
from
(
mManyWords
)
;
// 映射之后分发
obMap
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
map
(
mUpperLetterFunc
)
.
subscribe
(
mToastAction
)
;
|
from
是读取数组中的值, 每次单独分发, 并分发多次, 其余类似.
输入数组, 映射为单独分发, 并组合到一起, 集中显示.
1
2
3
4
5
6
|
// 优化过的代码, 直接获取数组, 再分发, 再合并, 再显示toast, Toast顺次执行.
Observable
.
just
(
mManyWordList
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
flatMap
(
mOneLetterFunc
)
.
reduce
(
mMergeStringFunc
)
.
subscribe
(
mToastAction
)
;
|
这次是使用
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表达式写法
* <p>
* Created by wangchenlong on 15/12/31.
*/
public
class
LambdaActivity
extends
Activity
{
@Bind
(
R
.
id
.
simple_tv_text
)
TextView
mTvText
;
final
String
[
]
mManyWords
=
{
"Hello"
,
"I"
,
"am"
,
"your"
,
"friend"
,
"Spike"
}
;
final
List
<String>
mManyWordList
=
Arrays
.
asList
(
mManyWords
)
;
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_simple
)
;
ButterKnife
.
bind
(
this
)
;
// 添加字符串, 省略Action的其他方法, 只使用一个onNext.
Observable
<String>
obShow
=
Observable
.
just
(
sayMyName
(
)
)
;
// 先映射, 再设置TextView
obShow
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
map
(
String
::
toUpperCase
)
.
subscribe
(
mTvText
::
setText
)
;
// 单独显示数组中的每个元素
Observable
<String>
obMap
=
Observable
.
from
(
mManyWords
)
;
// 映射之后分发
obMap
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
map
(
String
::
toUpperCase
)
.
subscribe
(
this
::
showToast
)
;
// 优化过的代码, 直接获取数组, 再分发, 再合并, 再显示toast, Toast顺次执行.
Observable
.
just
(
mManyWordList
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
flatMap
(
Observable
::
from
)
.
reduce
(
this
::
mergeString
)
.
subscribe
(
this
::
showToast
)
;
}
// 创建字符串
private
String
sayMyName
(
)
{
return
"Hello, I am your friend, Spike!"
;
}
// 显示Toast
private
void
showToast
(
String
s
)
{
Toast
.
makeText
(
LambdaActivity
.
this
,
s
,
Toast
.
LENGTH_SHORT
)
.
show
(
)
;
}
// 合并字符串
private
String
mergeString
(
String
s1
,
String
s2
)
{
return
String
.
format
(
"%s %s"
,
s1
,
s2
)
;
}
}
|
这次没有使用常规的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
compile
'com.squareup.retrofit:retrofit:2.0.0-beta2'
// Retrofit网络处理
compile
'com.squareup.retrofit:adapter-rxjava:2.0.0-beta2'
// Retrofit的rx解析库
compile
'com.squareup.retrofit:converter-gson:2.0.0-beta2'
// Retrofit的gson库
compile
'com.squareup.picasso:picasso:2.5.2'
// Picasso网络图片加载
|
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
|
/**
* Rx的网络请求方式
* <p>
* Created by wangchenlong on 15/12/31.
*/
public
class
NetworkActivity
extends
Activity
{
@Bind
(
R
.
id
.
network_rv_list
)
RecyclerView
mRvList
;
// 列表
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_network
)
;
ButterKnife
.
bind
(
this
)
;
// 设置Layout管理器
LinearLayoutManager
layoutManager
=
new
LinearLayoutManager
(
this
)
;
layoutManager
.
setOrientation
(
LinearLayoutManager
.
VERTICAL
)
;
mRvList
.
setLayoutManager
(
layoutManager
)
;
// 设置适配器
UserListAdapter
adapter
=
new
UserListAdapter
(
this
::
gotoDetailPage
)
;
NetworkWrapper
.
getUsersInto
(
adapter
)
;
mRvList
.
setAdapter
(
adapter
)
;
}
// 点击的回调
public
interface
UserClickCallback
{
void
onItemClicked
(
String
name
)
;
}
// 跳转到库详情页面
private
void
gotoDetailPage
(
String
name
)
{
startActivity
(
NetworkDetailActivity
.
from
(
NetworkActivity
.
this
,
name
)
)
;
}
}
|
在列表中提供点击用户信息跳转至用户详情.
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
|
/**
* 显示列表
* <p>
* Created by wangchenlong on 15/12/31.
*/
public
class
UserListAdapter
extends
RecyclerView
.
Adapter
<
UserListAdapter
.
UserViewHolder
>
{
private
List
<GitHubUser>
mUsers
;
// 用户名集合
private
NetworkActivity
.
UserClickCallback
mCallback
;
// 用户点击项的回调
public
UserListAdapter
(
NetworkActivity
.
UserClickCallback
callback
)
{
mUsers
=
new
ArrayList
<>
(
)
;
mCallback
=
callback
;
}
public
void
addUser
(
GitHubUser
user
)
{
mUsers
.
add
(
user
)
;
notifyItemInserted
(
mUsers
.
size
(
)
-
1
)
;
// 最后一位
}
@Override
public
UserViewHolder
onCreateViewHolder
(
ViewGroup
parent
,
int
viewType
)
{
View
item
=
LayoutInflater
.
from
(
parent
.
getContext
(
)
)
.
inflate
(
R
.
layout
.
item_network_user
,
parent
,
false
)
;
return
new
UserViewHolder
(
item
,
mCallback
)
;
}
@Override
public
void
onBindViewHolder
(
UserViewHolder
holder
,
int
position
)
{
holder
.
bindTo
(
mUsers
.
get
(
position
)
)
;
}
@Override
public
int
getItemCount
(
)
{
return
mUsers
.
size
(
)
;
}
// Adapter的ViewHolder
public
static
class
UserViewHolder
extends
RecyclerView
.
ViewHolder
{
@Bind
(
R
.
id
.
network_item_iv_user_picture
)
ImageView
mIvUserPicture
;
@Bind
(
R
.
id
.
network_item_tv_user_name
)
TextView
mTvUserName
;
@Bind
(
R
.
id
.
network_item_tv_user_login
)
TextView
mTvUserLogin
;
@Bind
(
R
.
id
.
network_item_tv_user_page
)
TextView
mTvUserPage
;
public
UserViewHolder
(
View
itemView
,
NetworkActivity
.
UserClickCallback
callback
)
{
super
(
itemView
)
;
ButterKnife
.
bind
(
this
,
itemView
)
;
// 绑定点击事件
itemView
.
setOnClickListener
(
v
->
callback
.
onItemClicked
(
mTvUserLogin
.
getText
(
)
.
toString
(
)
)
)
;
}
// 绑定数据
public
void
bindTo
(
GitHubUser
user
)
{
mTvUserName
.
setText
(
user
.
name
)
;
mTvUserLogin
.
setText
(
user
.
login
)
;
mTvUserPage
.
setText
(
user
.
repos_url
)
;
Picasso
.
with
(
mIvUserPicture
.
getContext
(
)
)
.
load
(
user
.
avatar_url
)
.
placeholder
(
R
.
drawable
.
ic_person_black_24dp
)
.
into
(
mIvUserPicture
)
;
}
}
// 用户类, 名称必须与Json解析相同
public
static
class
GitHubUser
{
public
String
login
;
public
String
avatar_url
;
public
String
name
;
public
String
repos_url
;
}
}
|
添加数据
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
|
/**
* 用户获取类
* <p>
* Created by wangchenlong on 15/12/31.
*/
public
class
NetworkWrapper
{
private
static
final
String
[
]
mFamousUsers
=
{
"SpikeKing"
,
"JakeWharton"
,
"rock3r"
,
"Takhion"
,
"dextorer"
,
"Mariuxtheone"
}
;
// 获取用户信息
public
static
void
getUsersInto
(
final
UserListAdapter
adapter
)
{
GitHubService
gitHubService
=
ServiceFactory
.
createServiceFrom
(
GitHubService
.
class
,
GitHubService
.
ENDPOINT
)
;
Observable
.
from
(
mFamousUsers
)
.
flatMap
(
gitHubService
::
getUserData
)
.
subscribeOn
(
Schedulers
.
newThread
(
)
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
subscribe
(
adapter
::
addUser
)
;
}
// 获取库信息
public
static
void
getReposInfo
(
final
String
username
,
final
RepoListAdapter
adapter
)
{
GitHubService
gitHubService
=
ServiceFactory
.
createServiceFrom
(
GitHubService
.
class
,
GitHubService
.
ENDPOINT
)
;
gitHubService
.
getRepoData
(
username
)
.
flatMap
(
Observable
::
from
)
.
subscribeOn
(
Schedulers
.
newThread
(
)
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
subscribe
(
adapter
::
addRepo
)
;
}
}
|
网络请求无法在主线程上执行, 需要启动异步线程, 如
Schedulers.newThread()
.
使用工厂模式ServiceFactory
创建服务, 也可以单独创建服务.
创建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
|
/**
* 用户获取类
* <p>
* Created by wangchenlong on 15/12/31.
*/
public
class
NetworkWrapper
{
private
static
final
String
[
]
mFamousUsers
=
{
"SpikeKing"
,
"JakeWharton"
,
"rock3r"
,
"Takhion"
,
"dextorer"
,
"Mariuxtheone"
}
;
// 获取用户信息
public
static
void
getUsersInto
(
final
UserListAdapter
adapter
)
{
GitHubService
gitHubService
=
ServiceFactory
.
createServiceFrom
(
GitHubService
.
class
,
GitHubService
.
ENDPOINT
)
;
Observable
.
from
(
mFamousUsers
)
.
flatMap
(
gitHubService
::
getUserData
)
.
subscribeOn
(
Schedulers
.
newThread
(
)
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
subscribe
(
adapter
::
addUser
)
;
}
// 获取库信息
public
static
void
getReposInfo
(
final
String
username
,
final
RepoListAdapter
adapter
)
{
GitHubService
gitHubService
=
ServiceFactory
.
createServiceFrom
(
GitHubService
.
class
,
GitHubService
.
ENDPOINT
)
;
gitHubService
.
getRepoData
(
username
)
.
flatMap
(
Observable
::
from
)
.
subscribeOn
(
Schedulers
.
newThread
(
)
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
subscribe
(
adapter
::
addRepo
)
;
}
}
|
这是Retrofit 2.0的写法, 注意需要添加Rx和Gson的解析.
设置网络请求的Url.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
/**
* GitHub的服务
* <p>
* Created by wangchenlong on 15/12/31.
*/
public
interface
GitHubService
{
String
ENDPOINT
=
"https://api.github.com"
;
// 获取个人信息
@GET
(
"/users/{user}"
)
Observable
<
UserListAdapter
.
GitHubUser
>
getUserData
(
@Path
(
"user"
)
String
user
)
;
// 获取库, 获取的是数组
@GET
(
"/users/{user}/repos"
)
Observable
<
RepoListAdapter
.
GitHubRepo
[
]
>
getRepoData
(
@Path
(
"user"
)
String
user
)
;
}
|
详情页面与主页类似, 参考代码, 不做细说.
线程安全
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
|
/**
* Rx的线程安全
* <p>
* Created by wangchenlong on 15/12/31.
*/
public
class
SafeActivity
extends
RxAppCompatActivity
{
private
static
final
String
TAG
=
"DEBUG-WCL: "
+
SafeActivity
.
class
.
getSimpleName
(
)
;
@Bind
(
R
.
id
.
simple_tv_text
)
TextView
mTvText
;
@Override
protected
void
onCreate
(
Bundle
savedInstanceState
)
{
super
.
onCreate
(
savedInstanceState
)
;
setContentView
(
R
.
layout
.
activity_simple
)
;
ButterKnife
.
bind
(
this
)
;
Observable
.
interval
(
1
,
TimeUnit
.
SECONDS
)
.
observeOn
(
AndroidSchedulers
.
mainThread
(
)
)
.
compose
(
bindToLifecycle
(
)
)
// 管理生命周期, 防止内存泄露
.
subscribe
(
this
::
showTime
)
;
}
private
void
showTime
(
Long
time
)
{
mTvText
.
setText
(
String
.
valueOf
(
"时间计数: "
+
time
)
)
;
Log
.
d
(
TAG
,
"时间计数器: "
+
time
)
;
}
@Override
protected
void
onPause
(
)
{
super
.
onPause
(
)
;
Log
.
w
(
TAG
,
"页面关闭!"
)
;
}
}
|
继承RxAppCompatActivity
, 添加bindToLifecycle
方法管理生命周期. 当页面onPause
时, 会自动结束循环线程. 如果注释这句代码, 则会导致内存泄露.
RxBinding
RxBinding是Rx中处理控件异步调用的方式, 也是由Square公司开发, Jake负责编写. 通过绑定组件, 异步获取事件, 并进行处理. 编码风格非常优雅.
除了RxJava, 再添加RxBinding的依赖.
1
2
3
4
|
// RxBinding
compile
'com.jakewharton.rxbinding:rxbinding:0.3.0'
compile
'com.jakewharton.rxbinding:rxbinding-appcompat-v7:0.3.0'
compile
'com.jakewharton.rxbinding:rxbinding-design:0.3.0'
|
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!
问啊-定制化IT教育平台,牛人一对一服务,有问必答,开发编程社交头条 官方网站:www.wenaaa.com 下载问啊APP,参与官方悬赏,赚百元现金。
QQ群290551701 聚集很多互联网精英,技术总监,架构师,项目经理!开源技术研究,欢迎业内人士,大牛及新手有志于从事IT行业人员进入!