AsyncTask是一个很经常使用的API,尤为异步处理数据并将数据应用到视图的操做场合。其实AsyncTask并非那么好,甚至有些糟糕。本文我会讲AsyncTask会引发哪些问题,如何修复这些问题,而且关于AsyncTask的一些替代方案。android
从Android API 3(1.5 Cupcake)开始,AsyncTask被引入用来帮助开发者更简单地管理线程。实际上在Android 1.0和1.1也是有相似的实现,那就是UserTask。UserTask和AsyncTask有着相同的API及实现,可是因为因为1.0和1.1的设备份额微乎其微,这里的概念就不会涉及到UserTask。并发
关于AsyncTask存在一个这样普遍的误解,不少人认为一个在Activity中的AsyncTask会随着Activity的销毁而销毁。而后事实并不是如此。AsyncTask会一直执行doInBackground()方法直到方法执行结束。一旦上述方法结束,会依据状况进行不一样的操做。异步
若是咱们的AsyncTask没有在Activity销毁时取消,这会致使AsyncTask崩溃,由于在onPostExecute(Result)方法中处理的视图已经再也不存在。ui
AsyncTask的cancel方法须要一个布尔值的参数,参数名为mayInterruptIfRunning,意思是若是正在执行是否能够打断
,若是这个值设置为true,表示这个任务能够被打断,不然,正在执行的程序会继续执行直到完成。若是在doInBackground()方法中有一个循环操做,咱们应该在循环中使用isCancelled()来判断,若是返回为true,咱们应该避免执行后续无用的循环操做。this
总之,咱们使用AsyncTask须要确保AsyncTask正确地取消。线程
若是你调用了AsyncTask的cancel(false),doInBackground()仍然会执行到方法结束,只是不会去调用onPostExecute()方法。可是实际上这是让应用程序执行了没有意义的操做。那么是否是咱们调用cancel(true)前面的问题就能解决呢?并不是如此。若是mayInterruptIfRunning设置为true,会使任务尽早结束,可是若是的doInBackground()有不可打断的方法会失效,好比这个BitmapFactory.decodeStream() IO操做。可是你能够提早关闭IO流并捕获这样操做抛出的异常。可是这样会使得cancel()方法没有任何意义。code
还有一种常见的状况就是,在Activity中使用非静态匿名内部AsyncTask类,因为Java内部类的特色,AsyncTask内部类会持有外部类的隐式引用。详细请参考细话Java:”失效”的private修饰符,因为AsyncTask的生命周期可能比Activity的长,当Activity进行销毁AsyncTask还在执行时,因为AsyncTask持有Activity的引用,致使Activity对象没法回收,进而产生内存泄露。对象
另外一个问题就是在屏幕旋转等形成Activity从新建立时AsyncTask数据丢失的问题。当Activity销毁并创新建立后,还在运行的AsyncTask会持有一个Activity的非法引用即以前的Activity实例。致使onPostExecute()没有任何做用。生命周期
关于AsyncTask时串行仍是并行有不少疑问,这很正常,由于它通过屡次的修改。若是你并不明白什么时串行仍是并行,能够经过接下来的例子了解,假设咱们在一个方法体里面有以下两行代码内存
new AsyncTask1().execute();new AsyncTask2().execute(); |
上面的两个任务时同时执行呢,仍是AsyncTask1执行结束以后,AsyncTask2才能执行呢?其实是结果依据API不一样而不一样。
在初版的AsyncTask,任务是串行调度。一个任务执行完成另外一个才能执行。因为串行执行任务,使用多个AsyncTask可能会带来有些问题。因此这并非一个很好的处理异步(尤为是须要将结果做用于UI试图)操做的方法。
后来Android团队决定让AsyncTask并行来解决1.6以前引发的问题,这个问题是解决了,新的问题又出现了。不少开发者实际上依赖于顺序执行的行为。因而不少并发的问题蜂拥而至。
好吧,开发者可能并不喜欢让AsyncTask并行,因而Android团队又把AsyncTask改为了串行。固然这一次的修改并无彻底禁止AsyncTask并行。你能够经过设置executeOnExecutor(Executor)来实现多个AsyncTask并行。关于API文档的描述以下
If we want to make sure we have control over the execution, whether it will run serially or parallel, we can check at runtime with this code to make sure it runs parallel:
public static void execute(AsyncTask as) {if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB_MR1) {as.execute();} else {as.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);}}//(This code does not work for API lvl 1 to 3) |
并不是如此,使用AsyncTask虽然能够以简短的代码实现异步操做,可是正如本文提到的,你须要让AsyncTask正常工做的话,须要注意不少条条框框。推荐的一种进行异步操做的技术就是使用Loaders。这个方法从Android 3.0 (Honeycomb)开始引入,在android支持包中也有包含。能够经过查看官方的文档来详细了解Loaders。
本次译文对原文有少部分删减修改处理。