前言
到本階段,相信各位碼友對RxJava的原理及操作符的使用方法已經基本掌握了。只是瞭解理論知識對於我們們程式猴來說當然遠遠不夠,理論運用到實踐才能出真知。一起來律動指尖到實際場景中看看怎麼運用RxJava。本篇我們演示一下如何運用RxJava從手機中獲取已安裝的第三方應用並通過RecyclerView展示出來。
準備工作
專案下build.gradle
buildscript {
......
dependencies {
classpath 'com.android.tools.build:gradle:2.3.2'
//ButterKnife支援
classpath 'com.jakewharton:butterknife-gradle-plugin:8.7.0'
}
}
......複製程式碼
app下build.gradle
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。
<?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用來展示每個應用的圖示及名稱。
<?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程式碼
實體類
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介面卡
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錯誤狀態及完成狀態的回撥方法。
瞭解的整個實現流程,接下來上程式碼:
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表示式達到簡化程式碼的作用,來看一下簡化之後的程式碼:
/**
* 初始化資料
*/
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