何谓异步任务
所谓异步任务(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
上边的例子就是解析一个比较庞大的Json。其实主要的功臣就是Schedulers.io()这个调度器,前面的just生成一个Observable只是一个壳而已。这里还是使用的Rxjava1,处理异常稍微有点麻烦,基本上就是成功传null失败传异常。Rxjava2里面异常处理将变得更加方便。
使用这种方法有几个好处:
- 有Rxjava强大的功能支持,可以对多个异步任务进行并行处理,或者顺序处理,或者花式处理;
- 链式逻辑,不用嵌套回调;
- 配一个Subscription可以很方便地取消任务;
- Schedulers.io()和AndroidSchedulers.mainThread()包办线程处理。
当然,我们可以甚至不要这个空壳,直接用该异步任务套一个Callable形成一个Observable:
Observable.fromCallable(new Callable
总而言之,使用Rxjava来处理异步任务,是我目前的选择。如果有更好的方法,我当然也会尝试。毕竟技术在不断发展。就我个人而言,我喜欢简单轻量的实现方法。