作为一个Android开发者,耳熟能详的一句话就是,Android的主线程不能做耗时操作,具体啥原因也不用多说了。 RxJava作为广泛应用的工具库,不应该没有对耗时操作的处理。对于多线程和并发编程应该有独特的理解。在之前的基础上,我们深入理解RxJava中的调度器(Schedulers)在Android开发中如何以响应式的方式创建网络操作,内存访问,以及耗时任务。
trampoline将会处理它的队列并且按序运行队列中每一个任务。它是repeat()和retry()方法默认的调度器。
每次我们调用storeBitmap(),RxJava处理创建所有它需要从I / O线程池一个特定的I/ O线程执行保存工作,避免了UI交互阻塞。
示例代码:
1.getApps是一个耗时操作,通过操作符subscribeOn使用Schedulers.io()调度器,将耗时操作在I/O线程中进行;
2.observeOn操作符以(AndroidSchedulers.mainThread())作为参数,表明返回的结果将在UI线程中操作
3.getApps()使用了onBackpressureBuffer操作符,主要是为了将获取到的信息缓存,在UI显示时,将数据及时发射。
再看AndroidSchedulers的构造方法,通过Scheduler main = hook.getMainThreadScheduler();其实是获取UI子线程,然后再赋值给mainThreadScheduler,我们调用时使用observeOn操作符,表明我们返回的结果应在UI子先处理
调度器(Schedulers)
io
Schedulers.io()调度器主要用于I/O操作,它基于根据需要,增长或缩减来自适应的线程池。大量的I/O调度操作将创建许多个线程并占用内存。一如既往的是,我们需要在性能和简捷两者之间找到一个有效的平衡点。computation
Schedulers.computation()调度是精于计算工作的,它也是许多RxJava方法的默认调度器 buffer(),debounce(),delay(),interval(),sample(),skip()。immediate
Schedulers.immediate()调度器主要用于立即在当前线程执行你指定的工作。它是timeout(),timeInterval(),以及timestamp()方法默认的调度器。newThread
Schedulers.newThread()调度器主要用于为指定任务启动一个新的线程。trampoline
Schedulers.trampoline()主要用于延迟工作任务的执行。当我们想在当前线程执行一个任务时,并不是立即,我们可以用.trampoline()将它入队。trampoline将会处理它的队列并且按序运行队列中每一个任务。它是repeat()和retry()方法默认的调度器。
非阻塞I/0操作的简单实现
大家都知道I/O操作是一个耗时操作,如果在主线程进行I/O操作,必然会影响UI操作的流畅性。比如保存照片的过程中放在主线程中进行,在保存的过程中,UI是不能进行交互的。该如何处理即能保存照片又能进行UI操作?大家都会想到开一个线程,将存储操作在线程中进行,不会影响UI交互。在RxJava中,我们习惯使用io调度器进行处理。<span style="font-size:14px;"> private static void blockingStoreBitmap(Context context, Bitmap bitmap, String filename) {
FileOutputStream fOut = null;
try {
fOut = context.openFileOutput(filename, Context.MODE_PRIVATE);
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);
fOut.flush();
fOut.close();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
try {
if (fOut != null) {
fOut.close();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
public static void storeBitmap(Context context, Bitmap bitmap, String filename) {
Schedulers
.io()
.createWorker()
.schedule(() -> {
blockingStoreBitmap(context, bitmap, filename);
});
}</span>
每次我们调用storeBitmap(),RxJava处理创建所有它需要从I / O线程池一个特定的I/ O线程执行保存工作,避免了UI交互阻塞。
SubscribeOn和ObserveOn
SubscribeOn
SubscribeOn操作符作用于每个Observable对象,subscribeOn操作符用Scheduler来作为参数并在这个Scheduler上执行Observable调用。observeOn
observeOn操作符将会在指定的调度器上返回结果,个人认为就是结果在哪个线程进行处理,比如UI子线程onBackpressureBuffer
onBackpressureBuffer操作符将监管Observable何时发射数据,如果Observable比观察者接收的数据要更快的话,它必须把它们存储在缓存中并提供一个合适的时间发射数据。示例代码:
<span style="font-size:14px;"> private Observable<AppInfo> getApps(){
return Observable.create(subscriber -> {
List<AppInfoRich> apps = new ArrayList<AppInfoRich>();
final Intent mainIntent = new Intent(Intent.ACTION_MAIN,null);
mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> infos = getActivity().getPackageManager().queryIntentActivities(mainIntent, 0);
for(ResolveInfo info : infos){
apps.add(new AppInfoRich(getActivity(),info));
}
for (AppInfoRich appInfo:apps) {
Bitmap icon = Utils.drawableToBitmap(appInfo.getIcon());
String name = appInfo.getName();
String iconPath = mFilesDir + "/" + name;
Utils.storeBitmap(App.instance, icon,name);
if (subscriber.isUnsubscribed()){
return;
}
subscriber.onNext(new AppInfo(name,iconPath,appInfo.getLastUpdateTime()));
}
if (!subscriber.isUnsubscribed()){
subscriber.onCompleted();
}
});
}
getApps()
.onBackpressureBuffer()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<AppInfo>() { [...]}</span>
从示例代码我们可以看出1.getApps是一个耗时操作,通过操作符subscribeOn使用Schedulers.io()调度器,将耗时操作在I/O线程中进行;
2.observeOn操作符以(AndroidSchedulers.mainThread())作为参数,表明返回的结果将在UI线程中操作
3.getApps()使用了onBackpressureBuffer操作符,主要是为了将获取到的信息缓存,在UI显示时,将数据及时发射。
AndroidSchedulers源码解析
<span style="font-size:14px;"> public final class AndroidSchedulers {
private static final AndroidSchedulers INSTANCE = new AndroidSchedulers();
private final Scheduler mainThreadScheduler;
private AndroidSchedulers() {
RxAndroidSchedulersHook hook = RxAndroidPlugins.getInstance().getSchedulersHook();
Scheduler main = hook.getMainThreadScheduler();
if (main != null) {
mainThreadScheduler = main;
} else {
mainThreadScheduler = new LooperScheduler(Looper.getMainLooper());
}
}
*******
}</span>
先纵观看下整个AndroidSchedulers实体类,AndroidSchedulers实例是以单例模式创建的,保证其唯一性,而且编译器在加载此类时,已经创建了其示例。再看AndroidSchedulers的构造方法,通过Scheduler main = hook.getMainThreadScheduler();其实是获取UI子线程,然后再赋值给mainThreadScheduler,我们调用时使用observeOn操作符,表明我们返回的结果应在UI子先处理
<span style="font-size:14px;"> private AndroidSchedulers() {
RxAndroidSchedulersHook hook = RxAndroidPlugins.getInstance().getSchedulersHook();
Scheduler main = hook.getMainThreadScheduler();
if (main != null) {
mainThreadScheduler = main;
} else {
mainThreadScheduler = new LooperScheduler(Looper.getMainLooper());
}
}</span>
AndroidSchedulers定义了两个静态方法mainThread()和from(Looper looper) ,这两个方法的返回值都是Scheduler,mainThread的返回值其实AndroidSchedulers中定义的一个不可修改Scheduler的值mainThreadScheduler,表明返回结果在 UI子线程处理。这个不用多说,它的返回值应该就是AndroidSchedulers实例在创建时,mainThreadScheduler的默认值<span style="font-size:14px;"> public static Scheduler from(Looper looper) {
if (looper == null) throw new NullPointerException("looper == null");
return new LooperScheduler(looper);
}</span>
从前面可以看出,AndroidSchedulers默认的处理线程可以认为UI子线程,如果返回的结果,不想在子线程处理,又该怎么处理呢?from(Looper looper)满足了我们这一意愿,通过new LooperScheduler(looper)创建调度器,实际上根据我们的实际预处理的线程中,处理返回的结果。LooperScheduler这个类其实继承了Scheduler这个抽象类。 LooperScheduler和Scheduler类涉及的内容,较多,这里不多做分析。
默认调度器
在RxJava中,某些Observable操作符的变体允许你设置用于操作执行的调度器,其它的则不在任何特定的调度器上执行,或者在一个指定的默认调度器上执行。下面的表格个列出了一些操作符的默认调度器: