异步消息处理机制——AsyncTask
AsyncTask的定义
AsyncTask是一个轻量级的异步框架,底层封装了Handler和线程池,它可以在线程池中执行耗时任务,然后把执行的进度和结果返回给主线程并在主线程中更新UI,适用于执行短时间的耗时任务。
AsyncTask的三个参数和四个方法
三个参数:
(1)Params
(2)Progress
(3)Result
四个方法:
(1)onPreExecute():执行耗时任务前初始化;
(2)doInBackground():执行耗时任务,运行在子线程,可调用publishProgress()更新任务进度;
(3)onProgressUpdate():进度UI更新;
(4)onPostExecute():耗时任务结束回调,接收Result处理结果;
AsyncTask的工作原理
两个线程池 + InternalHandler:
(1)串行线程池SerialExecutor:用于调度AsyncTask任务的排队执行顺序;
(2)ThreadPoolExecutot线程池:用于执行AsyncTask任务;
(3)InternalHandler:用于把AsyncTask从工作线程切换至主线程;
从AsyncTask的execute()方法开始分析,可以看到execute()实际上调用的是executeOnExecutor()方法,主要传入两个参数:sDefaultExecutor和params,sDefaultExecutor是一个串行的线程池,一个进程中所有AsyncTask任务都必须通过该线程池进行排队执行;
execute() sDefaultExecutor在executeOnExecutor()方法中,首先执行了onPreExecute()方法,接着执行线程池;具体的排队过程如下:
(1)首先参数Params会被封装成FutureTask对象,该对象是一个并行类,充当Runnable角色;
(2)FutureTask对象作为参数被SerialExecutor的execute(mFuture)方法调用;
(3)在SerialExecutor的execute(Runnable r)中,FutureTask对象通过offer()方法被加入到任务队列中;如果判断当前没有活动的AsyncTask任务,就会调用scheduleNext()方法执行任务队列的AsyncTask任务,依次执行任务队列中的AsyncTask任务直到队列为空;
最终真正执行AsyncTask任务的是scheduleNext()方法,该方法里通过线程池ThreadPoolExecutor.execute()方法执行任务;
负责调度AsyncTask任务排队执行的串行线程池SerialExecutor:
SerialExecutor.execute()负责执行AsyncTask任务的线程池ThreadPoolExecutor:
ThreadPoolExecutor知道了AsyncTask是通过SerialExecutor串行线程池进行任务排队以及通过ThreadPoolExecutor线程池执行真正任务之后,那么AsyncTask把Params参数传入后台执行耗时任务以及如何把Result结果通知主线程的呢?主要工作在AsyncTask的构造方法中体现:Params被传入doInBackground()方法中执行并返回执行结果Result,该方法是抽象方法,需要子类定义具体的耗时业务逻辑;处理结果Results通过postResult()方法进行UI更新。
AsyncTask构造方法postResult()主要是通过sHandler参数发送一个MESSAGE_POST_RESULT消息,sHandler参数就是一个InternalHandler,InternalHandler继承与Handler并实现了handleMessage(),主要实现AsyncTask从工作线程切换到主线程实现UI更新。
InternalHandler
AsyncTask在各版本的区别
1、Android1.6之前版本
AsyncTask首次引入时默认是串行执行的,在一个独立的线程中顺序执行任务,1.6版本以后才引入线程池。
2、Android1.6~Android3.0
(1)默认是并行执行任务的:直接把任务交给ThreadPoolExecutor线程池进行处理。
(2)使用的线程池不同:核心线程数为5,允许创建的最大线程数为128,采用LinkBlockingQueue阻塞队列,队列长度为10;如果同时启动10个任务,只有5个任务能立即执行,第六个任务需要等待,而且最多能容纳138个任务,当超过138个任务时就会执行饱和策略,抛出异常。
3、Android3.0以后版本
(1)默认是串行执行任务的:先把任务交给SerialExecutor线程池执行排队,再按排队顺序依次交给ThreadPoolExecutor线程池进行处理。
(2)使用的线程池不同:核心线程数和允许创建的最大线程数都是根据CPU的核数自动计算,采用LinkBlockingQueue阻塞队列,队列长度为10;所以不会出现饱和策略。
(3)可以使用executeOnExecutor并行处理任务:自定义配置线程池Executor并通过executeOnExecutor()方法可以并行处理任务;
使用AsyncTask的注意事项
1、内存泄漏:由于AsyncTask为非静态内部类,会持有外部activity类的匿名引用,所以当activity被销毁时内存回收失败,导致内存泄漏;
2、生命周期:在activity中创建的AsyncTask是不会随着activity的销毁而销毁的,因为在doInBackground()执行的后台任务,没有主动调用cancel()方法的话是不会被销毁的,也就是说在activity销毁前没有调用AsyncTask的cancel()方法,后台处理后的结果没办法更新到UI上,会导致系统奔溃。所以必须在activity的onDestroy()里面调用AsyncTask的cancel()进行销毁,保证程序的稳定;
3、结果丢失:由于屏幕旋转或者activity因系统内存不足被杀掉的原因,会导致activity重新创建,而之前运行的AsyncTask会持有一个已经失效的activity引用,所以当执行onPostExecute()更新界面时会出现结果丢失;
4、并行or串行:使用串行时会比较稳定,虽然效率上比并行要低;
参考:
《Android进阶之光》 第4章 4.5 AsyncTask的原理
《Android开发进阶:从小工到专家》 第3章 3.2.7 AsyncTask的原理
《Android开发艺术探索》第11章 11.2 Android中的线程形态