处理耗时的任务

我们已经知道如何处理缓慢的I/O操作。让我们看一个与I/O无关的耗时的任务。例如,我们修改loadList()函数并创建一个新的slow函数发射我们已安装的app数据。

  1. private Observable<AppInfo> getObservableApps(List<AppInfo> apps) {
  2. return Observable .create(subscriber -> {
  3. for (double i = 0; i < 1000000000; i++) {
  4. double y = i * i;
  5. }
  6. for (AppInfo app : apps) {
  7. subscriber.onNext(app);
  8. }
  9. subscriber.onCompleted();
  10. });
  11. }

正如你看到的,这个函数执行了一些毫无意义的计算,只是针对这个例子消耗时间,然后从List<AppInfo>对象中发射我们的AppInfo数据,现在,我们重排loadList()函数如下:

  1. private void loadList(List<AppInfo> apps) {
  2. mRecyclerView.setVisibility(View.VISIBLE);
  3. getObservableApps(apps)
  4. .subscribe(new Observer<AppInfo>() {
  5. @Override
  6. public void onCompleted() {
  7. mSwipeRefreshLayout.setRefreshing(false);
  8. Toast.makeText(getActivity(), "Here is the list!", Toast.LENGTH_LONG).show();
  9. }
  10. @Override
  11. public void onError(Throwable e) {
  12. Toast.makeText(getActivity(), "Something went wrong!", Toast.LENGTH_SHORT).show();
  13. mSwipeRefreshLayout.setRefreshing(false);
  14. }
  15. @Override
  16. public void onNext(AppInfo appInfo) {
  17. mAddedApps.add(appInfo);
  18. mAdapter.addApplication(mAddedApps.size() - 1, appInfo);
  19. }
  20. });
  21. }

如果我们运行这段代码,当我们点击Navigation Drawer菜单项时App将会卡住一会,然后你能看到下图中半关闭的菜单:

处理耗时的任务 - 图1

如果我们不够走运的话,我们可以看到下图中经典的ANR信息框:

处理耗时的任务 - 图2

可以确定的是,我们将会看到下面在logcat中不愉快的信息:

  1. I/Choreographer Skipped 598 frames! The application may be doing too much work on its main thread.

这条信息比较清楚,Android在告诉我们用户体验非常差的原因是我们用不必要的工作量阻塞了UI线程。但是我们已经知道了如何处理它:我们有调度器!我们只须添加几行代码到我们的Observable链中就能去掉加载慢和Choreographer信息:

  1. getObservableApps(apps)
  2. .onBackpressureBuffer()
  3. .subscribeOn(Schedulers.computation())
  4. .observeOn(AndroidSchedulers.mainThread())
  5. .subscribe(new Observer<AppInfo>() { [...]

用这几行代码,我们将可以快速关掉Navigation Drawer,一个漂亮的进度条,一个工作在独立的线程缓慢执行的计算任务,并在主线程返回结果让我们更新已安装的应用列表。