一、什么是AsyncTask 异步任务
AsyncTask这个类,就是为了方便我们在后台线程中执行操作,然后将结果发送给主线程,从而在主线程中进行UI更新等操作。在使用AsyncTask时,我们无需关注ThreadHandler。AsyncTask内部会对其进行管理,这样我们就只需要关注于我们的业务逻辑即可。
默认是一个串行的线程池SerialExecutor
二、AsyncTask的使用方法
AsyncTask有四个重要的回调方法,分别是:onPreExecute、doInBackground, onProgressUpdate 和 onPostExecute。这四个方法会在AsyncTask的不同时期进行自动调用,我们只需要实现这几个方法的内部逻辑即可。
1、三个参数
Params表示用于AsyncTask执行任务的参数的类型
Progress表示在后台线程处理的过程中,可以阶段性地发布结果的数据类型
Result表示任务全部完成后所返回的数据类型
2、五个方法
onPreExecute(),该方法有MainThread注解,表示该方法是运行在主线程中的。在AsyncTask执行了execute()方法后就会在UI线程上执行onPreExecute()方法,该方法在task真正执行前运行,我们通常可以在该方法中显示一个进度条,从而告知用户后台任务即将开始。
doInBackground(),执行在工作线程中,该方法用于在工作线程中执行耗时任务。其返回值为任务全部完成后所返回的数据类型。在这个方法中可以调用publishProgress()方法,该方法会将阶段性的结果发布出去,将结果传递到UI线程,执行onProgressUpdate()方法。
doInBackground执行完毕后,就会将结果传递到UI线程,将执行结果传递到onPostExecute()方法中。
最后调用AysnaTask.execute()方法开始执行任务,参数类型为需要执行任务的参数类型,比如一个图片的url。
执行execute()方法之后就执行了onProExecute()方法,最后开始执行mFuture方法。
postResultIfNotInvoked()的执行,参考AsynaTask内部原理。
三、AsynaTask内部原理
1、AsynaTask 初始化一些参数,通过下面代码和注释我们可以知道,AsyncTask初始化了一些参数,并用这些参数实例化了一个线程池THREAD_POOL_EXECUTOR,需要注意的是该线程池被定义为public static final,由此我们可以看出AsyncTask内部维护了一个静态的线程池,默认情况下,AsyncTask的实际工作就是通过该THREAD_POOL_EXECUTOR完成的。
2、下面简单介绍一下ThreadPoolExecutor的一些参数介绍。
3、执行完上述代码后,又创建了一个异步框架SerialExecutor。SerialExecutor实现了Executor接口中的execute方法,该类用于串行执行任务,即一个接一个地执行任务,而不是并行执行任务。里面有一个双队列的数据结构ArrayDeque。
sDefaultExecutor表示AsyncTask执行任务时默认所使用的线程池,sDefaultExecutor的初始值为SERIAL_EXECUTOR,表示默认情况下AsyncTask是串行执行任务,而不是并行执行任务
4、AsyncTask内部定义了一个Statusa枚举类型,表示任务执行的状态。一个AsyncTask正常情况下会经历PENDING->RUNNING->FINISHED三个状态。
5、AsyncTask 里面使用的是InternalHanker ,里面绑定了主线程的Looper和消息队列。
如果handler收到的是MESSAGE_POST_RESULT消息,就会执行finish()方法,最后调用onPostExecute()方法。
如果handler收到的是MESSAGE_POST_PROGRESS消息,就是执行onProfressUpdate()方法。
在publishProgress方法中,就使用了Handler发送了MESSAGE_POST_PROGRESS消息。
6、AsyncTask无论任务完成还是取消任务,FutureTask都会执行done方法,如下所示:
无论任务正常执行完成还是任务取消,都会执行postResultIfNotInvoked方法。postResultIfNotInvoked代码如下所示:最终发送一个handler消息,切换到UI线程,任务执行完毕。
四、AsynaTask的注意事项
一、内存泄漏
1、调用AsynaTask.cancel()方法。
采用正确的方式应该采用静态内部类结合软引用的方式。可以比较好的避免内存泄漏。
但是使用static类型的同样需要我们将在asyncTask中使用的变量也声明为static,数量少的情况下还是可以接受的,但是很多的static情况下,容易让内存吃紧,GC频繁。所以,合适的解决方案就是使用弱引用来改变内部类对外部类的引用关系。
可以看到上面我们定义了一个WeakAsyncTask类继承自AsyncTask。在类中我们使用WeakRefrences来弱引用外部Context。所以,当外部类需要释放时,会立刻被GC所回收。避免了内存泄漏的问题。
二、生命周期
有时候我们会认为在Activity中创建的AsyncTask内部类会随着Activity的销毁而被销毁结束。其实并不是这样,AsyncTask会一直执行,直到doInBackground方法执行完成。此时,如果执行cancel(boolean)操作,onCancelled(Result result) 方法会被执行。这样AsyncTask占据的内存才会被通知回收。否则,会继续执行onPostExecute(Result result) 方法。如果我们的Activity销毁之前,没有取消 AsyncTask,这有可能让我们的AsyncTask崩溃(crash)。也就是说我们在Activity销毁之前要手动cancel掉AsyncTask。因为此时AsyncTask已经没有存在的意义了。
另外,即使我们正确地调用了cancle() 也未必能真正地取消任务。因为如果在doInBackgroud里有一个不可中断的操作,比如BitmapFactory.decodeStream(),那么这个操作会继续下去。
三、结果丢失
在Activity销毁之前cancel掉AsyncTask。即中断AsyncTask的执行,因为此时已经没有必要让AsyncTask继续执行了。
结果丢失,屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
四、串行还是并行
这个
修改了好多次,目前27版本是这个样子的,默认是串行的
默认的是一个串行的线程。
如果想要并行执行,可以这样。传入AsynTask 自带的线程池。AsyncTask.THREAD_POOL_EXECUTOR