安卓异步任务浅谈

何谓异步任务

所谓异步任务(AsyncTask),主要是对于一些比较费时间的操作,不能立即产生结果,而是要等一段时间,在结果出来之后再对其进行处理。因为安卓主线程负责处理UI的关系,如果在主线程上进行这些费时操作,会造成UI卡顿,因此异步任务一般都是交给后台线程处理,假如需要更新UI的话就等处理结束再转交结果给主线程。
这里谈的异步任务,并不仅限于安卓的异步任务类,而是泛指这一类需要进行异步回调的任务。

AsyncTask类介绍

安卓提供了一个AsyncTask类来处理异步任务:

private class DownloadFilesTask extends AsyncTask {
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }

     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }

     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }
// to run it, use
new DownloadFilesTask().execute(url1, url2, url3);
 

看起来很科学,在任务准备、开始、执行、完成都有回调可以使用。
但是为什么如今基本没人用了呢?
因为AsyncTask的线程池不是可以修改的,而原有的封装好的并不完美。在异步任务较多的时候可能会出问题。也有文章指出超过138个异步任务就会造成崩溃。此外,异步任务的嵌套、异常处理都不那么方便。
经过本人的实践,对于一般情况,AsyncTask其实也还是可以用,但是写起来总是觉得很冗长,而遇到多层回调和错误处理更是痛苦。

出路

对于我个人而言,自从接触了Volley、OkHttp等网络访问库之后,我就没再用过AsyncTask。这些网络库都能实现异步回调。例如:

new CallFeedBack().postFeedBack(new OkHttpCallback() {
            @Override
            public void onSuccess(JSONObject response) {
                Timber.d("feedback post response: " + response.toString());
                try {
                    if (response.getJSONObject("meta").getInt("code") == 200) {
                        // do stuff
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                    // handle exception
                }
            }

            @Override
            public void onFail(Exception e) {
                Timber.e("feedback post failed: " + e.toString());
                // handle error
            }
        }, map);

但不管怎么说,这些毕竟是网络库,对于非网络访问的异步任务怎么办?比如解析一个很大的Json?
我们知道,可以通过扩展java.lang.Thread类或者实现Runnable接口来调用后台线程,可以考虑把这些任务放在那里。但问题在于,这些不自带回调,假如需要回调还得自己再加,总而言之太复杂。
那么说了这么多,解决办法是什么呢?就是Rxjava——Rxjava大法好!

Rxjava与异步任务

如果只是为了异步任务,用Rxjava有点杀鸡用牛刀的感觉,毕竟这只是其强大功能的冰山一角。而且现在Rxjava已经到了第2版,功能更加完善。
不过,就让我们看看Rxjava是怎么对付异步任务的:

private void parseResponse(final JSONObject responseJSONObject) {
                    Observable.just("").map(new Func1() {
                        @Override
                        public Object call(String s) {
                            try {
                                T.t("parsing activity response json");
                                postsList.clear();
                                JSONArray postsArray = responseJSONObject.getJSONArray(WebServices.Response.POSTS);
                                UniversalPreferences.getInstance().put(PrefsKeys.LOCATION_POSTS, postsArray.toString());
                                GsonBuilder gsonBuilder = new GsonBuilder();
                                BooleanSerializer serializer = new BooleanSerializer();
                                gsonBuilder.registerTypeAdapter(Boolean.class, serializer);
                                gsonBuilder.registerTypeAdapter(boolean.class, serializer);
                                gsonBuilder.setDateFormat(Constants.DATE_TIME_FORMAT);
                                Gson gson = gsonBuilder.create();
                                for (int i = 0; i < postsArray.length(); i++) {
                                    Post post = gson.fromJson(postsArray.get(i).toString(), Post.class);
                                    postsList.add(post);
                                }
                                finalPostList.clear();
                                finalPostList.addAll(filterPosts(filterOptions, postsList));
                            } catch (JSONException e) {
                                return Observable.error(e);
                            }
                            return null;
                        }
                    }).subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(new Action1() {
                                @Override
                                public void call(Object o) {
                                    if (o == null){
                                        adapter.notifyDataSetChanged();
                                    }
                                    displayRelevantControl();
                                }
                            });
                }
 
 

上边的例子就是解析一个比较庞大的Json。其实主要的功臣就是Schedulers.io()这个调度器,前面的just生成一个Observable只是一个壳而已。这里还是使用的Rxjava1,处理异常稍微有点麻烦,基本上就是成功传null失败传异常。Rxjava2里面异常处理将变得更加方便。
使用这种方法有几个好处:

  • 有Rxjava强大的功能支持,可以对多个异步任务进行并行处理,或者顺序处理,或者花式处理;
  • 链式逻辑,不用嵌套回调;
  • 配一个Subscription可以很方便地取消任务;
  • Schedulers.io()和AndroidSchedulers.mainThread()包办线程处理。

当然,我们可以甚至不要这个空壳,直接用该异步任务套一个Callable形成一个Observable:

Observable.fromCallable(new Callable() {
                        @Override
                        public Object call() throws Exception {
                            T.t("parsing activity response json");
                            postsList.clear();
                            JSONArray postsArray = responseJSONObject.getJSONArray(WebServices.Response.POSTS);
                            UniversalPreferences.getInstance().put(PrefsKeys.LOCATION_POSTS, postsArray.toString());
                            GsonBuilder gsonBuilder = new GsonBuilder();
                            BooleanSerializer serializer = new BooleanSerializer();
                            gsonBuilder.registerTypeAdapter(Boolean.class, serializer);
                            gsonBuilder.registerTypeAdapter(boolean.class, serializer);
                            gsonBuilder.setDateFormat(Constants.DATE_TIME_FORMAT);
                            Gson gson = gsonBuilder.create();
                            for (int i = 0; i < postsArray.length(); i++) {
                                Post post = gson.fromJson(postsArray.get(i).toString(), Post.class);
                                postsList.add(post);
                            }
                            finalPostList.clear();
                            finalPostList.addAll(filterPosts(filterOptions, postsList));
                            return null;
                        }
                    }).subscribeOn(Schedulers.io())
                            .observeOn(AndroidSchedulers.mainThread())
                            .subscribe(new Action1() {
                                @Override
                                public void call(Object o) {
                                    if (o == null){
                                        adapter.notifyDataSetChanged();
                                    }
                                    displayRelevantControl();
                                }
                            });
 
 

总而言之,使用Rxjava来处理异步任务,是我目前的选择。如果有更好的方法,我当然也会尝试。毕竟技术在不断发展。就我个人而言,我喜欢简单轻量的实现方法。

你可能感兴趣的:(安卓异步任务浅谈)