基于最新版本的AsyncTask详细解读主要是一些AsyncTask的原理解读,本篇将分析AsyncTask使用的受限部,这里可能会有一些你以前没注意到的点,同时提供一个替代方案AsyncTaskScheduler。
一些关于AsyncTask的指责
如果搜索关键词"AsyncTask的缺陷",会有很多关于AsyncTask缺陷的文章,很多是基于以下几个问题,但这些问题真的是AsyncTask自身的问题还是使用不当导致的呢?这真的需要好好分析一下,不能人云亦云,强行甩锅。
-
生命周期和内存泄漏
"当Activity结束或者退出应用时AsyncTask会一直执行doInBackground()方法直到方法执行结束,这可能会导致在onPostExecute时view不存在而导致崩溃溃,以及可能的内存泄露"。
如果退出Activity时AsyncTask扔在执行,上面说的的却会发生,但这些问题需要由使用者来解决而不是AsyncTask来解决,因为AsyncTask只是执行后台任务,它怎么知道你什么时候要终止,要退出,确定使用Handler时不会出现这样问题???
所以应该在相应的生命周期如onDestory调用cancel取消任务,上面的问题就不会发生了,所有这个锅不应该AsyncTask来背。
cancel不能正常取消的问题
首先调用cancel终止AsyncTask的原理是对执行异步任务的线程调用interrupt()函数。
首先,每个线程内部都有一个boolean型变量表示线程的中断状态,true代表线程处于中断状态,false表示未处于中断状态。
而interrupt()方法的作用只是用来改变线程的中断状态(把线程的中断状态改为true,即被中断)。因此interrupt()方法代表着外界希望中断此线程,只是希望,具体怎么处理还是线程内部来做,一般情况下interrupt()方法可以使处于阻塞状态的线程抛出InterruptedException从而结束阻塞状态或则判断Thread.interrupted()来处理逻辑。所以如果你的AsyncTask后台任务有未做中断的处理肯定会一直执行这个线程。所以这需要你自己在doInbackground里进行中断处理,即使你认为这是个缺陷也应该是Thread类的缺陷,因为要用到线程处理异步任务,AsyncTask无法选择其他方式,而且调用cancel后onPostExecute也不会在执行了,不会导致UI线程的问题,所以这个锅也不应该AsyncTask来背。Activity意外重启,状态消失问题
比如当用户旋转屏幕的时候Activity就会重新启动,如果之前有AsyncTask正在异步加载处理数据,那么之前的数据就会消失,而新的AsyncTask重新创建,这的却是个问题,但你用其他的方式进行请求同样会发生这个问题啊。
其实可能是我们的要求太多了,AsyncTask只是一个处理异步任务的工具,很多逻辑上的东西需要我们自己来处理,就像使用Handler和Thread,不正确处理同样会出现上述问题,这些总得来说就是异步带来的问题,这是一个时间和性能的选择问题,AsyncTask就是简化了包装了Handler的处理步骤而已。上面的这些更应该是一些重要的注意事项,而不是AsyncTask的问题
实际存在的问题
- 并行串行问题
上面的一些可能是由于使用不当导致的,但并行串行问题方面AsyncTask问题很大。
看AsyncTask源码文档时看到这样建议"AsyncTasks should ideally be used for short operations (a few seconds at the most",就是尽量执行一个短时间的任务,最对也就几秒的任务。当初还很疑惑,AsyncTask这玩意不就是用来处理后台任务的吗,又不是在主线程,为什么还限制短时间的任务,那要你何用啊。
基于最新版本的AsyncTask详细解读分析过在api11后AsyncTask默认的是串行执行任务,基本现上市面上的设备上都将是串行执行。自己可以写个Demo试试看。
这些串行执行共用的AsyncTask的是一个线程池,这真的很严重。因为是顺序执行,导致你调用execute() 可能 没法立刻执行,也可能就执行不了,因为谁知道有没有其他的AsyncTask任务在执行啊,或者任务还是个很耗时的任务,或者就是个while(ture)循环或者for(;;)来一直处理一种后台任务,那么同一进程内的AsyncTask在这之后调用execute的都将无法执行。
想当初初学Android时觉得AsyncTask真是个方便的东西,手机写了个基于socket通信的应用,doInbackground处理,然后直接通知UI。在doInbackground函数里处理一些连接以及数据流的接收及发送,socket的等待连接和数据结束都是阻塞的啊,现在回想起来当初真是年轻😃。
当然你可以立刻执行一个任务通过调用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)通过AsyncTask的THREAD_POOL_EXECUTOR线程池或者传入其他的线程池来立刻执行任务,但THREAD_POOL_EXECUTOR有最大并发数的限制,具体分析见基于最新版本的AsyncTask详细解读,但只执行一个任务时通过线程池来管理的也是无奈之举,而且这种方式不是默认方案啊,不一发现啊。
错误处理问题
AsyncTask没有对发生的一些异常进行处理,你只能在onBackground里进行一些判断,但之外的一些异常情况发生你都无法了解,比如线程异常退出等。多个任务的管理问题
如果需要多个后台任务,需要新建多个AsyncTask来执行任务,在需要退出的时候你需要对每一个都进行一定的处理来避免内存泄露以及UI问题,这是一个很麻烦的事情。
如果你使用AsyncTask默认的执行方式,出了问题都很难排查。你可以保证你能正确使用AsyncTask,但你没法保证别人也能正确使用啊,这就是别人给你挖的坑,但是你跳了进去啊,关键你可能都不知道到底哪个AsyncTask在执行,可能引用就发生在第三方库的也有可能啊。
替代方案
基于上述实际存在的问题尤其是并行串行问题,写了一个类似AsyncTask的库AsyncTaskScheduler,处理了上述的一些实际存在的问题。
细节介绍及使用