前言
本文介绍如何使用 subscribeOn/observeOn
来实现后台执行耗时任务,并通知主线程更新进度。
效果图
应用场景
当我们需要进行一些耗时操作,例如下载、访问数据库等,为了不阻塞主线程,往往会将其放在后台进行处理,同时在处理的过程中、处理完成后通知主线程更新 UI,这里就涉及到了后台线程和主线程之间的切换。首先回忆一下,在以前我们一般会用以下两种方式来实现这一效果:
- 创建一个新的子线程,在其
run()
方法中执行耗时的操作,并通过一个和主线程 Looper 关联的 Handler 发送消息给主线程更新进度显示、处理结果。 - 使用
AsyncTask
,在其doInBackground
方法中执行耗时的操作,调用 publishProgress 方法通知主线程,然后在onProgressUpdate
中更新进度显示,在 onPostExecute 中显示最终结果。
具体实现
1. 添加依赖
//Butter Knife
implementation 'com.jakewharton:butterknife:10.2.0'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.0'
//TitleBar
implementation 'com.github.getActivity:TitleBar:8.6'
//RxJava
implementation 'io.reactivex.rxjava2:rxjava:2.2.10'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
2. 布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical">
<com.hjq.bar.TitleBar
android:id="@+id/title_bar"
android:layout_width="match_parent"
android:background="@color/teal_200"
android:layout_height="?android:attr/actionBarSize"
app:title="后台执行耗时操作,实时通知 UI 更新"
app:titleStyle="bold"
app:titleSize="18sp"
app:backButton="false"
app:titleColor="@color/white"/>
<Button
android:id="@+id/btn_download"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/ic_button_bg"
android:layout_marginTop="20dp"
android:layout_marginLeft="14dp"
android:layout_marginRight="14dp"
android:textSize="18sp"
android:textColor="@color/white"
android:text="开始下载" />
<TextView
android:id="@+id/tv_download_result"
android:gravity="center"
android:layout_marginTop="12dp"
android:layout_width="match_parent"
android:layout_height="40dp"/>
</LinearLayout>
3. 逻辑代码
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";
@BindView(R.id.btn_download)
Button mTvDownload;
@BindView(R.id.tv_download_result)
TextView mTvResult;
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
@Override
protected int getLayoutId() {
return R.layout.activity_main;
}
@Override
protected void initView() {
}
@OnClick(R.id.btn_download)
public void onViewClicked() {
startDownload();
}
private void startDownload() {
final Observable<Integer> observable = Observable.create(new ObservableOnSubscribe<Integer>() {
@Override
public void subscribe(ObservableEmitter<Integer> e) throws Exception {
for (int i = 0; i < 100; i++) {
if (i % 20 == 0) {
try {
Thread.sleep(500); //模拟下载的操作。
} catch (InterruptedException exception) {
if (!e.isDisposed()) {
e.onError(exception);
}
}
e.onNext(i);
}
}
e.onComplete();
}
});
DisposableObserver<Integer> disposableObserver = new DisposableObserver<Integer>() {
@Override
public void onNext(Integer value) {
Log.d(TAG, "onNext=" + value);
mTvResult.setText("当前下载进度:" + value);
}
@Override
public void onError(Throwable e) {
Log.d(TAG, "onError=" + e);
mTvResult.setText("下载失败");
}
@Override
public void onComplete() {
Log.d(TAG, "onComplete");
mTvResult.setText("下载成功");
}
};
observable.subscribeOn(Schedulers.single()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
mCompositeDisposable.add(disposableObserver);
}
@Override
protected void onDestroy() {
super.onDestroy();
mCompositeDisposable.clear();
}
}