RxJava2-后台执行耗时操作,实时通知 UI 更新(一)
一、前言
接触RxJava2
已经很久了,也看了网上的很多文章,发现基本都是在对RxJava
的基本思想介绍之后,再去对各个操作符进行分析,但是看了之后感觉过了不久就忘了。
偶然的机会看到了开源项目 RxJava-Android-Samples,这里一共介绍了十六种RxJava2
的使用场景,它从实际的应用场景出发介绍RxJava2
的使用,特别适合对于RxJava2
已经有初步了解的开发者进一步地去学习如何将其应用到实际开发当中。
因此,我打算跟着这个项目的思路编写一系列实战的介绍并完成示例代码编写,并对该实例中用到的知识进行介绍,做到学以致用。下面,就开始第一个例子的学习,源码的仓库为:RxSample。
二、示例
2.1 应用场景
当我们需要进行一些耗时操作,例如下载、访问数据库等,为了不阻塞主线程,往往会将其放在后台进行处理,同时在处理的过程中、处理完成后通知主线程更新UI
,这里就涉及到了后台线程和主线程之间的切换。首先回忆一下,在以前我们一般会用以下两种方式来实现这一效果:
- 创建一个新的子线程,在其
run()
方法中执行耗时的操作,并通过一个和主线程Looper
关联的Handler
发送消息给主线程更新进度显示、处理结果。 - 使用
AsyncTask
,在其doInBackground
方法中执行耗时的操作,调用publishProgress
方法通知主线程,然后在onProgressUpdate
中更新进度显示,在onPostExecute
中显示最终结果。
那么,让我们看一些在RxJava
中如何完成这一需求。
2.2 示例代码
我们的界面上有一个按钮mTvDownload
,点击之后会发起一个耗时的任务,这里我们用Thread.sleep
来模拟耗时的操作,每隔500ms
我们会将当前的进度通知主线程,在mTvDownloadResult
中显示当前处理的进度。
public class BackgroundActivity extends AppCompatActivity {
private TextView mTvDownload;
private TextView mTvDownloadResult;
private CompositeDisposable mCompositeDisposable = new CompositeDisposable();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_background);
mTvDownload = (TextView) findViewById(R.id.tv_download);
mTvDownloadResult = (TextView) findViewById(R.id.tv_download_result);
mTvDownload.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
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("BackgroundActivity", "onNext=" + value);
mTvDownloadResult.setText("Current Progress=" + value);
}
@Override
public void onError(Throwable e) {
Log.d("BackgroundActivity", "onError=" + e);
mTvDownloadResult.setText("Download Error");
}
@Override
public void onComplete() {
Log.d("BackgroundActivity", "onComplete");
mTvDownloadResult.setText("Download onComplete");
}
};
observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
mCompositeDisposable.add(disposableObserver);
}
@Override
protected void onDestroy() {
super.onDestroy();
mCompositeDisposable.clear();
}
}
实际的运行结果如下:

三、示例解析
3.1 线程切换
在上面的例子中,涉及到了两种类型的操作:
- 需要在后台执行的耗时操作,对应于
subscribe(ObservableEmitter<Integer> e)
中的代码。 - 需要在主线程进行
UI
更新的操作,对应于DisposableObserver
的所有回调,具体的是在onNext
中进行进度的更新;在onComplete
和onError
中展示最终的处理结果。
那么,这两种类型操作所运行的线程是在哪里指定的呢,关键是下面这句:
observable.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(disposableObserver);
subscribeOn(Schedulers.io())
:指定observable
的subscribe
方法运行在后台线程。observeOn(AndroidSchedulers.mainThread())
:指定observer
的回调方法运行在主线程。
这两个函数刚开始的时候很有可能弄混,我是这么记的,subscribeOn
以s
开头,可以理解为“上游”开头的谐音,也就是上游执行的线程。
关于这两个函数,还有一点说明:多次调用subscribeOn
,会以第一次的为准;而多次调用observeOn
则会以最后一次的为准,不过一般我们都不会这么干,就不举例子了。
3.2 线程的类型
subscribeOn/observeOn
都要求传入一个Schedulers
的子类,它就代表了运行线程类型,下面我们来看一下都有哪些选择:
Schedulers.computation()
:用于计算任务,默认线程数等于处理器的数量。Schedulers.from(Executor executor)
:使用Executor
作为调度器,关于Executor
框架可以参考这篇文章:多线程知识梳理(5) - 线程池四部曲之 Executor 框架。Schedulers.io( )
:用于IO
密集型任务,例如访问网络、数据库操作等,也是我们最常使用的。Schedulers.newThread( )
:为每一个任务创建一个新的线程。Schedulers.trampoline( )
:当其它排队的任务完成后,在当前线程排队开始执行。Schedulers.single()
:所有任务共用一个后台线程。
以上是在io.reactivex.schedulers
包中,提供的Schedulers
,而如果我们导入了下面的依赖,那么在io.reactivex.android.schedulers
下,还有额外的两个Schedulers
可选:
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
AndroidSchedulers.mainThread()
:运行在应用程序的主线程。AndroidSchedulers.from(Looper looper)
:运行在该looper
对应的线程当中。
3.3 使用 CompositeDisposable 对下游进行管理
如果Activity
要被销毁时,我们的后台任务没有执行完,那么就会导致Activity
不能正常回收,而对于每一个Observer
,都会有一个Disposable
对象用于管理,而RxJava
提供了一个CompositeDisposable
类用于管理这些Disposable
,我们只需要将其将入到该集合当中,在Activity
的onDestroy
方法中,调用它的clear
方法,就能避免内存泄漏的发生。
四、小结
这个系列的第一篇文章,我们介绍了如何使用subscribeOn/observeOn
来实现后台执行耗时任务,并通知主线程更新进度。
RxJava2-后台执行耗时操作,实时通知 UI 更新(一)的更多相关文章
- Winform 界面执行耗时操作--UI卡顿假死问题
UI卡顿假死问题 误区1:使用不同的线程操作UI控件和耗时操作(即,跨线程操作UI控件CheckForIllegalCrossThreadCalls = false;), 注意:此处只是为了记录... ...
- ASP.NET服务器端执行耗时操作的工作记录
公司之前有这样一个业务需求: 一名同事做出文件a0和b0,然后将a0加密为a1.b0加密为b1:再将文件a0.a1.b0和b1上传至服务器M:同时要将服务器N上的数据表添加一条记录,该记录的ID就是前 ...
- Parallel 类并行任务(仅仅当执行耗时操作时,才有必要使用)
using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using S ...
- C# WinForm 异步执行耗时操作并将过程显示在界面中
private void button3_Click(object sender, EventArgs e) { RunAsync(() => ...
- 主线程不能执行耗时的操作,子线程不能更新Ui
在Android项目中经常有碰到这样的问题,在子线程中完成耗时操作之后要更新UI,下面就自己经历的一些项目总结一下更新的方法: 在看方法之前看一下Android中消息机制: 引用 Message:消息 ...
- C#.NET使用Task,await,async,异步执行控件耗时事件(event),不阻塞UI线程和不跨线程执行UI更新,以及其他方式比较
使用Task,await,async,异步执行事件(event),不阻塞UI线程和不跨线程执行UI更新 使用Task,await,async 的异步模式 去执行事件(event) 解决不阻塞UI线程和 ...
- AsyncTask 进行耗时操作和UI 更新
相信各位对 AsyncTask 不会陌生,虽然它有如下弊端: 1. 如果在activiy内部new 一个AsyncTask, 横竖屏切换生成一个新的activity,等结果返回时,处理不好容易出现NP ...
- winform 开发中 把耗时操作 封装起来 异步执行(.net 4.0)
.先定义一个 BackgroundTask.cs 代码如下: public class BackgroundTask { private static WaitDialogForm LoadingDl ...
- 网络请求怎么样和UI线程交互? Activity2怎么通知Activity1 更新数据
1.网络请求怎么样和UI线程交互? 目前我的做法是,建立线程池管理网络请求线程,通过添加task来新增网络请求.所有的网络操作通过统一的request来实现,网络返回结果通过回调onError和onS ...
随机推荐
- IDEA手工添加webapp目录
自己手工建目录,是没法识别的,在自己手工建的webapp文件夹上右键菜单,Make Directory As也没有相应的选项 解决方案是 File->Project Structure
- Pipenv和Python虚拟环境
Pipenv & 虚拟环境 本教程将引导您完成安装和使用 Python 包. 它将向您展示如何安装和使用必要的工具,并就最佳做法做出强烈推荐.请记住, Python 用于许多不同的目的.准确地 ...
- Android support 26.0.0-alpha1 产生的问题(zz)
针对以下两个错误 Java.lang.NoClassDefFoundError: Failed resolution of: Landroid/support/v4/animation/Animato ...
- MUI框架开发HTML5手机APP(二)--页面跳转传值&底部选项卡切换
概 述 JRedu 在上一篇博客中,我们学习了如何使用Hbuilder创建一个APP,同时如何使用MUI搭建属于自己的第一款APP,没有学习的同学可以戳链接学习: http://www.cnblo ...
- 【ASP.NET Core】从向 Web API 提交纯文本内容谈起
前些时日,老周在升级“华南闲肾回收登记平台”时,为了扩展业务,尤其是允许其他开发人员在其他平台向本系统提交有关肾的介绍资料,于是就为该系统增加了几个 Web API. 其中,有关肾的介绍采用纯文本方式 ...
- C# System.Threading.Timer
提供以指定的时间间隔对线程池线程执行方法的机制 using System; using System.Threading; class TimerExample { static void Main( ...
- C# C/S程序出错:ContextSwitchDeadlock is detected
选择菜单栏[调试]->[窗口]->[异常设置] 使用快捷键Ctrl + Alt + E,可以快速打开该对话框 通过取消勾选或者勾选进行设置即可. https://blog.csdn.net ...
- 论如何优雅的自定义ThreadPoolExecutor线程池
更好的markDown阅读体验可直接访问我的CSDN博客:https://blog.csdn.net/u012881584/article/details/85221635 前言 线程池想必大家也都用 ...
- 基于jQuery可悬停控制图片轮播代码
基于jQuery可悬停控制图片轮播代码.这是一款可悬停切换全屏轮播jQuery幻灯片.效果图如下: 在线预览 源码下载 实现的代码: <!-- 轮播广告 --> <div id= ...
- IOS-企业开发人员账号&邓白氏码申请记录
Apple开发人员账号分三种,个人.公司,还有企业.个人和公司都称为标准账号. 另一种是教育机构的账号. 账号介绍 个人和公司的就不说了.如今仅仅说企业账号 首先是申请企业账号的地址: https:/ ...