RxJava终章之实践出真知(七)

前言

  到本阶段,相信各位码友对RxJava的原理及操作符的使用方法已经基本掌握了。只是了解理论知识对于咱们程序猴来说当然远远不够,理论运用到实践才能出真知。一起来律动指尖到实际场景中看看怎么运用RxJava。本篇我们演示一下如何运用RxJava从手机中获取已安装的第三方应用并通过RecyclerView展示出来。
  

准备工作

项目下build.gradle

1
2
3
4
5
6
7
8
9
buildscript {
......
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
//ButterKnife支持
classpath 'com.jakewharton:butterknife-gradle-plugin:8.7.0'
}
}
......

app下build.gradle

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
apply plugin: 'com.android.application'
apply plugin: 'com.jakewharton.butterknife'
android {
......
defaultConfig {
......
//Lambda支持
jackOptions {
enabled true
}
}
//Lambda支持
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
......
}
dependencies {
......
//RxJava支持
compile 'io.reactivex:rxjava:1.0.14'
compile 'io.reactivex:rxandroid:1.0.1'
//RecyclerView支持
compile 'com.android.support:recyclerview-v7:25.3.1'
//ButterKnife支持
compile 'com.jakewharton:butterknife:8.7.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.7.0'
}

布局

主布局

  主布局没啥好说的,就是一个RecyclerView。

1
2
3
4
5
6
7
8
9
10
11
12
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
tools:context="com.holmeslei.rxjavademo.ui.PracticeActivity>
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_app_list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>

RecyclerView的Item布局

  RecyclerView单个条目布局,我们需要一个ImageView及一个TextView用来展示每个应用的图标及名称。

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
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="80dp"
android:paddingLeft="20dp">
<ImageView
android:id="@+id/item_iv_head"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_centerVertical="true"
android:scaleType="fitXY"
android:src="@mipmap/ic_launcher" />
<TextView
android:id="@+id/item_iv_app_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="30dp"
android:layout_toRightOf="@+id/item_iv_head"
android:text="微信"
android:textColor="#555555"
android:textSize="18sp" />
</RelativeLayout>
</RelativeLayout>

Java代码

实体类

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
public class AppInfo {
private String appName; //应用名称
private Drawable appIcon; //应用图标
public String getAppName() {
return appName;
}
public void setAppName(String appName) {
this.appName = appName;
}
public Drawable getAppIcon() {
return appIcon;
}
public void setAppIcon(Drawable appIcon) {
this.appIcon = appIcon;
}
@Override
public String toString() {
return "AppInfo{" +
"appName='" + appName + '\'' +
", appIcon=" + appIcon +
'}';
}
}

RecyclerView适配器

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
public class AppInfoListAdapter extends RecyclerView.Adapter<AppInfoListAdapter.MyViewHolder> {
private Context context;
private List<AppInfo> appInfoList;
public AppInfoListAdapter(Context context, List<AppInfo> appInfoList) {
this.context = context;
this.appInfoList = appInfoList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_app_list, parent, false);
return new MyViewHolder(view);
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
//设置应用图标
holder.ivHead.setImageDrawable(appInfoList.get(position).getAppIcon());
//设置应用名称
holder.tvAppName.setText(appInfoList.get(position).getAppName());
}
@Override
public int getItemCount() {
return appInfoList.size();
}
class MyViewHolder extends RecyclerView.ViewHolder {
@BindView(R.id.item_iv_head)
ImageView ivHead;
@BindView(R.id.item_iv_app_name)
TextView tvAppName;
MyViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}

Activity

  核心来了,我们重点看initData()方法。分析一下需求,要获取手机中已安装的第三方应用并展示出来,主要分以下几步:

  1. 从系统中获取所有应用列表数据的集合List<ApplicationInfo>
  2. 这个集合是已安装的所有应用集合,我们只需要其中的第三方应用,所以要使用到RxJava的filter操作符进行过滤。
  3. 由于ApplicationInfo不满足我们的需求,需要将其转换为我们自定义的实体类AppInfo,所以要使用到RxJava的map操作符进行转换。
  4. 由于获取应用集合,过滤,转换的过程可能是耗时的,我们需要指定Observable运行在io线程。
  5. 由于获取到满足条件的数据后我们还需刷新UI进行展示,所以还需要指定Observer运行在Android的UI线程。
  6. 最后还需要输出错误日志,及完成之后的刷新UI,所以需要重写RxJava错误状态及完成状态的回调方法。
  了解的整个实现流程,接下来上代码:

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
public class PracticeActivity extends AppCompatActivity {
@BindView(R.id.rv_app_list)
RecyclerView rvAppList;
private AppInfoListAdapter adapter;
private List<AppInfo> appInfoList = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_practice);
ButterKnife.bind(this);
initRecyclerView();
initData();
}
/**
* 初始化RecyclerView
*/
private void initRecyclerView() {
LinearLayoutManager manager = new LinearLayoutManager(this);
rvAppList.setLayoutManager(manager);
adapter = new AppInfoListAdapter(this, appInfoList);
rvAppList.setAdapter(adapter);
}
/**
* 初始化数据
*/
private void initData() {
final PackageManager pm = getPackageManager();
//获取所有应用信息集合
List<ApplicationInfo> infoList = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
Observable.from(infoList)
//过滤出已安装的第三方应用
.filter(new Func1<ApplicationInfo, Boolean>() {
@Override
public Boolean call(ApplicationInfo applicationInfo) {
return (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0;
}
})
//转换为自定义的AppInfo类
.map(new Func1<ApplicationInfo, AppInfo>() {
@Override
public AppInfo call(ApplicationInfo applicationInfo) {
AppInfo appInfo = new AppInfo();
appInfo.setAppIcon(applicationInfo.loadIcon(pm));
appInfo.setAppName(applicationInfo.loadLabel(pm).toString());
return appInfo;
}
})
//Observable被观察者执行在io线程
.subscribeOn(Schedulers.io())
//Observer观察者执行在AndroidUI线程
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<AppInfo>() {
@Override
public void onCompleted() {
//更新列表UI
adapter.notifyDataSetChanged();
}
@Override
public void onError(Throwable e) {
//显示错误信息
Toast.makeText(PracticeActivity.this, e.getMessage(),
Toast.LENGTH_LONG).show();
}
@Override
public void onNext(AppInfo appInfo) {
//添加第三方应用数据到集合
appInfoList.add(appInfo);
}
});
}

Lambda简化

  还记得我在RxJava系列第一篇中提到过吗?RxJava可结合Lambda表达式达到简化代码的作用,来看一下简化之后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/**
* 初始化数据
*/
private void initData() {
final PackageManager pm = getPackageManager();
List<ApplicationInfo> infoList = pm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES);
Observable.from(infoList)
.filter(applicationInfo -> (applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) <= 0)
.map(applicationInfo -> {
AppInfo appInfo = new AppInfo();
appInfo.setAppIcon(applicationInfo.loadIcon(pm));
appInfo.setAppName(applicationInfo.loadLabel(pm).toString());
return appInfo;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
appInfo -> appInfoList.add(appInfo),
throwable -> Toast.makeText(PracticeActivity.this, throwable.getMessage(), Toast.LENGTH_LONG).show(),
() -> adapter.notifyDataSetChanged()
);
}

  是不是看起来清爽简洁呢?不太了解Lambda表达式的码友可跳转到我的另一篇讲解Lambda表达式的文章:
  Lambda表达式基本语法与应用

运行效果


总结

  到此,RxJava系列从理论到运用再到实践,整个过程我们通过了7篇文章来学习。然而RxJava的知识远远不止这些,这就需要各位码友去探索发掘了。本系列只是达到入门RxJava的程度,且是基于RxJava1.0版本进行讲解的。目前RxJava已经更新到了2.0+,与1.0版本也有不小的改动与优化的地方。后期我会专门对RxJava2.x有何改动开一篇文章进行讲解,敬请期待。
  技术渣一枚,有写的不对的地方欢迎大神们留言指正,有什么疑惑或者建议也可以在我Github上RxJavaDemo项目Issues中提出,我会及时回复。
  附上RxJavaDemo的地址:
  RxJavaDemo   

坚持原创技术分享,您的支持是我前进的动力,谢谢!