Android异步任务AsyncTask的使用与原理分析

在了解了 Android缓存机制后我准备自己动手写一个LruCache和DiskLruCache二级缓存的轻量级的图片请求框架,在思考如何搭建这个框架时,纠结于用何种方式去下载图片,是直接new出一个线程呢,还是用看起来稍微高大上档次一点的AsyncTask异步任务来处理?思来想去,还是虚荣心作怪,还是用AsyncTask吧,正好这个工具类我之前用的也比较少,对它的原理也不是很清楚,趁这个机会,好好学一下AsyncTask的使用,并分析一下其 源码实现。待分析完AsyncTask之后,接着完成图片请求框架的编写。 

1、AsyncTask的使用

分析AsyncTask原理之前,还是好好学习一下它的具体使用方法。

1.1 AsyncTask简介

在Android中,我们更新UI的操作必须要在主线程(UI线程)中进行,而下载图片、文件这种操作必须要在子线程中进行,Android为我们提供了Handler机制,实现了子线程与主线程之间的通信。通常做法就是先new出一个子线程Thread,在子线程中完成下载操作后,通过handler发送一条Message给主线程,主线程收到消息后,就可以进行UI的更新工作了,如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
Handler mHadler = new Handler(){
     @Override
     public void handleMessage(Message msg) {
         super .handleMessage(msg);
         if (msg.what == 1 ){
             Bitmap bitmap = (Bitmap) msg.obj;
             //更新UI...
         }
     }
};
 
private void download(){
     new Thread( new Runnable() {
         @Override
         public void run() {
             // 这里进行下载操作...获得了图片的bitmap
             //下载完后才,向主线程发送Message
             Message msg = Message.<em>obtain</em>();
             msg.obj = bitmap;
             msg.what = 1 ; //区分哪一个线程发送的消息
             mHadler.sendMessage(msg);
         }
     }).start();
}
可以看到,每次要进行下载工作,我们就得先创建出Thread,然后在主线程中写好handler,为了对这个过程进行封装,Android提供了AsyncTask异步任务,AsyncTask对线程和handler进行了封装,使得我们可以直接在AsyncTask中进行UI的更新操作,就好像是在子线程进行UI更新一样。 

1.2 创建AsyncTask子类

AsyncTask是一个抽象类,我们必须写一个子类继承它,在子类中完成具体的业务下载操作。为了对各种情况更好的封装,AsyncTask抽象类指定了三个泛型参数类型,如下:
?
1
public abstract class AsyncTask<params, result= "" >{ ... }</params,>
其中,三个泛型类型参数的含义如下:  Params:开始异步任务执行时传入的参数类型,即doInBackground()方法中的参数类型; Progress:异步任务执行过程中,返回下载进度值的类型,即在doInBackground中调用publishProgress()时传入的参数类型; Result:异步任务执行完成后,返回的结果类型,即doInBackground()方法的返回值类型;  有了这三个参数类型之后,也就控制了这个AsyncTask子类各个阶段的返回类型,如果有不同业务,我们就需要再另写一个AsyncTask的子类进行处理。 

1.3 AsyncTask的回调方法

前面我们说过,AsyncTask对线程和handler进行了封装,那它的封装性体现在哪里呢?其实,AsyncTask的几个回调方法正是这种封装性的体现,使得我们感觉在子线程进行UI更新一样。一个基本的AsyncTask有如下几个回调方法:  (1)onPreExecute():在执行后台下载操作之前调用,运行在主线程中; (2)doInBackground():核心方法,执行后台下载操作的方法,必须实现的一个方法,运行在子线程中;  (3)onPostExecute():后台下载操作完成后调用,运行在主线程中;   因此,AsyncTask的基本生命周期过程为: onPreExecute() --> doInBackground() --> onPostExecute()。其中,onPreExecute()和onPostExecute()分别在下载操作前和下载操作后调用,同时它们是在主线程中进行调用,因此可以在这两个方法中进行UI的更新操作,比如,在onPreExecute()方法中,将下载等待动画显示出来,在onPostExecute()方法中,将下载等待动画进行隐藏。 如果我们想向用户展示文件的下载进度情况,这时,我们可以在doInBackground()下载操作中,调用publishProgress(),将当前进度值传入该方法,而publishProgress()内部会去调用AsyncTask的另一个回调方法: (4)onProgressUpdate():在下载操作doInBackground()中调用publishProgress()时的回调方法,用于更新下载进度,运行在主线程中; 因此,在需要更新进度值时,AsyncTask的基本生命周期过程为: onPreExecute() --> doInBackground() --> publishProgress() -->onProgressUpdate()--> onPostExecute()。可以看到,AsyncTask的优秀之处在于几个回调方法的设置上,只有donInBackground()是运行在子线程的,其他三个回调方法都是在主线程中运行,因此,只要在AsyncTask中,就可以实现文件的后台下载、UI的更新操作。 好了,明白了如何创建一个AsyncTask,以及AsyncTask的几个回调方法的调用时机,我们就可以来实战体验一下。在例子中,我们去下载一张图片,并通过进度条显示下载的进度。首先实现一个AsyncTask的具体实现类,进行图片的下载,如下:  
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
public class MyAsyncTask extends AsyncTask<string, bitmap= "" >{
     private ProgressBar mPreogressBar; //进度条
     private ImageView mImageView; //图片显示控件
 
     public MyAsyncTask(ProgressBar pb,ImageView iv){
         mPreogressBar = pb;
         mImageView = iv;
     }
 
     @Override
     protected void onPreExecute() {
         super .onPreExecute();
         mPreogressBar.setVisibility(View.<em>VISIBLE</em>);
     }
 
     @Override
     protected Bitmap doInBackground(String... params) {
         String urlParams = params[ 0 ]; //拿到execute()传过来的图片url
         Bitmap bitmap = null ;
         URLConnection conn = null ;
         InputStream is = null ;
         try {
             URL url = new URL(urlParams);
             conn = url.openConnection();
             is = conn.getInputStream();
 
             //这里只是为了演示更新进度的功能,实际的进度值需要在从输入流中读取时逐步获取
             for ( int i = 0 ; i < 100 ; i++){
                 publishProgress(i);
                 Thread.<em>sleep</em>( 50 ); //为了看清效果,睡眠一段时间
             }
             //将获取到的输入流转成Bitmap
             BufferedInputStream bis = new BufferedInputStream(is);
             bitmap = BitmapFactory.<em>decodeStream</em>(bis);
 
             is.close();
             bis.close();
         } catch (IOException e) {
             e.printStackTrace();
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         return bitmap;
     }
 
     @Override
     protected void onProgressUpdate(Integer... values) {
         super .onProgressUpdate(values);
         mPreogressBar.setProgress(values[ 0 ]);
     }
 
     @Override
     protected void onPostExecute(Bitmap bitmap) {
         super .onPostExecute(bitmap);
         mPreogressBar.setVisibility(View.<em>GONE</em>);
         mImageView.setImageBitmap(bitmap);
     }
}</string,>
上面doInBackground()中获取进度值时,我们只是为了做一个进度值更新调用的演示,实际项目文件下载中,我们可能会对拿到的输入流进行处理,比如读取输入流将文件保存到本地,在读取输入流的时候,我们就可以获取到已经读取的输入流大小作为进度值了,如下:
?
1
2
3
4
5
6
7
8
9
10
11
//实际项目中如何获取文件大小作为进度值及更新进度值
             int totalSize = conn.getContentLength(); //获取文件总大小
             int size = 0 ; //保存当前下载文件的大小,作为进度值
             int count = 0 ;
             byte [] buffer = new byte [ 1024 ];
             while ((count = is.read(buffer)) != - 1 ){
                 size += count; //获取已下载的文件大小
                 //调用publishProgress更新进度,它内部会回调onProgressUpdate()方法
                 publishProgress(size,totalSize);
                 Thread.sleep( 100 ); //为了看清效果,睡眠一段时间
             }
在MainActivity中使用:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class MainActivity extends AppCompatActivity {
     private ImageView mImageView;
     private ProgressBar mProgressBar;
     private MyAsyncTask asyncTask;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.<em>image</em>);
 
         mImageView = (ImageView) findViewById(R.id.<em>id_image</em>);
         mProgressBar = (ProgressBar) findViewById(R.id.<em>pb</em>);
 
         asyncTask = new MyAsyncTask(mProgressBar, mImageView);
         asyncTask.execute(<em>URL</em>); //将图片url作为参数传入到doInBackground()中
     }
}
布局文件如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
<!--?xml version= "1.0" encoding= "utf-8" ?-->
<relativelayout br= "" xmlns:android= "http://schemas.android.com/apk/res/android" >    android:padding= "16dp"
     android:layout_width= "match_parent"
     android:layout_height= "match_parent" >
     <imageview<br>        android:id= "@+id/id_image"
         android:layout_width= "match_parent"
         android:layout_height= "match_parent" />
     <progressbar<br>        android:id= "@+id/pb"
         style= "@style/Widget.AppCompat.ProgressBar.Horizontal"
         android:layout_centerInParent= "true"
         android:layout_width= "match_parent"
         android:layout_height= "30dp" />
</progressbar<br></imageview<br></relativelayout>
效果如下: \ 由于需要联网,注意在AndroidManifest.xml中加入网络访问权限。 

1.4 取消下载任务

我们先来看两个现象。 我们在布局中加一个按钮,点击这个按钮再加载一次图片,布局如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<!--?xml version= "1.0" encoding= "utf-8" ?-->
<relativelayout br= "" xmlns:android= "http://schemas.android.com/apk/res/android" >    android:padding= "16dp"
     android:layout_width= "match_parent"
     android:layout_height= "match_parent" >
     <imageview<br>        android:id= "@+id/id_image"
         android:layout_width= "match_parent"
         android:layout_height= "match_parent" />
     <progressbar<br>        android:id= "@+id/pb"
         style= "@style/Widget.AppCompat.ProgressBar.Horizontal"
         android:layout_centerInParent= "true"
         android:layout_width= "match_parent"
         android:layout_height= "30dp" />
     <button<br>        android:layout_width= "match_parent"
         android:layout_height= "wrap_content"
         android:padding= "15dp"
         android:id= "@+id/id_btn"
         android:text= "加载图片"
         android:textSize= "16sp"
         android:layout_alignParentBottom= "true"
         android:onClick= "loadImage" />
</button<br></progressbar<br></imageview<br></relativelayout>
因此,在MainActivity中,我们就需要加入loadImage方法,如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class MainActivity extends AppCompatActivity {
     private ImageView mImageView;
     private ProgressBar mProgressBar;
     private MyAsyncTask asyncTask;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super .onCreate(savedInstanceState);
         setContentView(R.layout.<em>image</em>);
 
         mImageView = (ImageView) findViewById(R.id.<em>id_image</em>);
         mProgressBar = (ProgressBar) findViewById(R.id.<em>pb</em>);
 
         asyncTask = new MyAsyncTask(mProgressBar, mImageView);
         asyncTask.execute(<em>url</em>);
}
 
     public void loadImage(View v){
         asyncTask.execute(<em>url</em>);
     }
}
现象一:在loadImage()方法中,我们直接再次通过asyncTask.execute()执行加载。看看此时效果如何: \onCreate中初始加载完一次图片后,我们点击“加载图片”按钮,此时程序直接崩溃了!这是因为,每一个new出的AsyncTask只能执行一次execute(),如果同一个AsyncTask多次执行execute()执行将会报错。 现象二:我们来修改loadImage()方法,在该方法中,我们在打开自身MainActivity,使得多次初始化的时候进行加载,如下:
?
1
2
3
4
public void loadImage(View v){
     Intent i = new Intent( this ,MainActivity. class );
     startActivity(i);
}
此时效果如下: \在第一次运行程序进入MainActivity,执行execute但在显示出图片之前,立即点击“加载图片”按钮,新打开一个MainActivity,我们发现这个MainActivity的进度条没有立即展示出进度出来,说明这个MainActivity的AsyncTask没有立即执行doInBackground(),这是因为AsyncTask内部使用的是线程池,相当于里面有一个线程队列,执行一次execute时会将一个下载任务加入到线程队列,只有前一个任务完成了,下一个下载任务才会开始执行。为了达到我们想要的效果,我们自然想到把上一个任务给取消掉。的确,AsyncTask为我们提供了cancel()方法来取消一个任务的执行,但是要注意的是,cancel方法并没有能力真正去取消一个任务,其实只是设置这个任务的状态为取消状态,我们需要在doInBackground()下载中进行检测,一旦检测到该任务被用户取消了,立即停止doInBackground()方法的执行。我们先修改MainActivity,根据不同业务需求,在不同地方进行任务的取消,我们这里在onPause()中进行任务的取消,在MainActivity方法中加入onPause()方法,如下:
?
1
2
3
4
5
6
7
8
@Override
protected void onPause() {
     super .onPause();
     if (asyncTask != null && asyncTask.getStatus() == AsyncTask.Status.<em>RUNNING</em>){
         //cancel只是将对应的任务标记为取消状态
         asyncTask.cancel( true );
     }
}
继续修改AsyncTask,在这里面进行任务是否被取消的检测,这里我们只简单修改下doInBackground()和onProgressUpdae()方法,实际项目中开自己的业务逻辑来控制,如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@Override
protected Bitmap doInBackground(String... params) {
     String urlParams = params[ 0 ]; //拿到execute()传过来的图片url
     Bitmap bitmap = null ;
     URLConnection conn = null ;
     InputStream is = null ;
     try {
         URL url = new URL(urlParams);
         conn = url.openConnection();
         is = conn.getInputStream();
 
         for ( int i = 0 ; i < 100 ; i++){
             if (isCancelled()){ //通过isCancelled()判断任务任务是否被取消
                 break ;
             }
             publishProgress(i);
             Thread.<em>sleep</em>( 50 ); //为了看清效果,睡眠一段时间
         }
         BufferedInputStream bis = new BufferedInputStream(is);
         bitmap = BitmapFactory.<em>decodeStream</em>(bis);
 
         is.close();
         bis.close();
     } catch (IOException e) {
         e.printStackTrace();
     } catch (InterruptedException e) {
         e.printStackTrace();
     }
     return bitmap;
}
 
@Override
protected void onProgressUpdate(Integer... values) {
     super .onProgressUpdate(values);
     if (isCancelled()){ //通过isCancelled()判断任务任务是否被取消
         return ;
     }
     mPreogressBar.setProgress(values[ 0 ]);
}
在doInBackground()的for循环更新进度过程中,我们持续不断的监听任务十分被取消,一旦取消了,尽快退出doInBackground的执行,现在运行效果如下: \可以看到,现在每次点击“加载图片”按钮,新的界面都会立即更新进度条,我们就业把前面的任务给取消掉了。  使用小结:(1)AsyncTask中,只有doInBackground()方法是处于子线程中运行的,其他三个回调onPreExecute()、onPostExecute()、onProgressUpdate()都是在UI线程中进行,因此在这三个方法里面可以进行UI的更新工作;(2)每一个new出的AsyncTask只能执行一次execute()方法,多次运行将会报错,如需多次,需要新new一个AsyncTask;(3)AsyncTask必须在UI线程中创建实例,execute()方法也必须在UI线程中调用; 

2、AsyncTask内部实现原理

进入AsyncTask源码,简单分析一下AsyncTask的内部实现。前面说到,AsyncTask内部封装了异步任务队列和Handler,那我们就从这两块入手。 

2.1 AsyncTask的内部异步队列

在我们上面那个例子中,讲到第二个现象时,就可以直观的看到,AsyncTask的内部肯定是基于工作队列这种方式的,每次执行execute()就会把当前的任务加入到工作队列中。但是大家有没有发现这么个问题,我每次点击“加载图片”按钮,都会进入MainActivity的onCreate方法中去新建一个AsyncTask的对象,既然对象都是新的了,为什么AsyncTask中的工作队列还是能够正常管理呢?这就说明,AsyncTask内部的线程池、工作队列的定义都应该是static的,而static定义的变量是属于进程范围内的,只有这样,这些static的变量才能交给AsyncTask这个类来管理,而不是AsyncTask的具体子类对象。如下部分变量声明:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
         private final AtomicInteger mCount = new AtomicInteger( 1 );
         public Thread newThread(Runnable r) {
             return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
         }
     };
 
     private static final BlockingQueue<runnable> sPoolWorkQueue =
             new LinkedBlockingQueue<runnable>( 128 );
 
     /**
      * 真正用来执行任务的的线程池
      */
     public static final Executor THREAD_POOL_EXECUTOR
             = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                     TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
 
     /**
      * 定义一个线程池,在线程池中有一个Runnable任务队列,用来存放、顺序执行任务
      */
     public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
 
     //AsyncTask内部默认使用的线程池
     private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
     //自定义的一个Handler
     private static InternalHandler sHandler;</runnable></runnable>
可以看到,优线程池有关的都定义为了static类型,其中,SERIAL_EXECUTOR内部就定义了一个任务队列ArrayDequemTasks,当我们调用AsyncTask的execute()时,就会将当前任务加入到该队列中。先来看执行AsyncTask的execute()时的情景:
?
1
 
?
1
2
3
4
@MainThread
public final AsyncTask<params, result= "" > execute(Params... params) {
return executeOnExecutor(<em>sDefaultExecutor</em>, params);
}</params,>
AsyncTask的execute()交给了executeOnExecutor()方法,将将默认的线程池作为参数传进来,进入executeOnExecutor方法中:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@MainThread
public final AsyncTask<params, result= "" > executeOnExecutor(Executor exec,
         Params... params) {
     if (mStatus != Status.<em>PENDING</em>) { //1、这里判断当前AsyncTask是否正在执行或已执行完毕
         switch (mStatus) {
             case <em>RUNNING</em>:
                 throw new IllegalStateException( "Cannot execute task:"
                         + " the task is already running." );
             case <em>FINISHED</em>:
                 throw new IllegalStateException( "Cannot execute task:"
                         + " the task has already been executed "
                         + "(a task can be executed only once)" );
         }
     }
     mStatus = Status.<em>RUNNING</em>; //2、设置正在执行的状态
     onPreExecute(); //3、回调onPreExecute()方法
     mWorker.mParams = params;
     exec.execute(mFuture); //4、放到前面默认构造的线程池中去执行
     return this ;
}</params,>
上面第一步,先判断当前AsyncTask是否正在运行或已经执行完毕,如果正在执行或执行完毕再次执行将抛出异常,这也正是我们前面在使用的时候谈到,同一个AsyncTask不能多次进行execute()的原因!到了第三步的时候,先去调用一下onPreExecute()方法,因为executeOnExecutor方法本身就是在UI线程中运行的,所以onPreExecute也会在UI线程中运行。第四步,才会开始讲当前AsyncTask任务加入到队列中,我们进入默认的线程池中去看一下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
private static class SerialExecutor implements Executor {
     final ArrayDeque<runnable> mTasks = new ArrayDeque<runnable>();
     Runnable mActive;
     public synchronized void execute( final Runnable r) {
         mTasks.offer( new Runnable() {
             public void run() {
                 try {
                     r.run();
                 } finally {
                     scheduleNext();
                 }
             }
         });
         if (mActive == null ) {
             scheduleNext();
         }
     }
 
     protected synchronized void scheduleNext() {
         if ((mActive = mTasks.poll()) != null ) {
             <em>THREAD_POOL_EXECUTOR</em>.execute(mActive);
         }
     }
}</runnable></runnable>
在第四步执行execute时,实际就是调用的SerialExecutor中的execute方法,在这里面,先创建了一个Runnable对象,然后将这个Runnable对象添加到任务队列mTasks中,在当执行到这个Runnable时调用scheduleNext去队列中取出一个任务,然后交给另一个线程池去真正执行这个任务。 

2.2 与UI线程进行交互-handler

AsyncTask内部通过自定义的static类型的InternalHandler和UI线程进行交互。InternalHandler实现如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
private static class InternalHandler extends Handler {
     public InternalHandler() {
         super (Looper.<em>getMainLooper</em>());
     }
 
     @SuppressWarnings ({ "unchecked" , "RawUseOfParameterizedType" })
     @Override
     public void handleMessage(Message msg) {
         AsyncTaskResult<!--?--> result = (AsyncTaskResult<!--?-->) msg.obj;
         switch (msg.what) {
             case <em>MESSAGE_POST_RESULT</em>:
                 // There is only one result
                 result.mTask.finish(result.mData[ 0 ]);
                 break ;
             case <em>MESSAGE_POST_PROGRESS</em>:
                 result.mTask.onProgressUpdate(result.mData);
                 break ;
         }
     }
}
每当收到子线程发来的和UI线程进行通信的handler请求时,先从Message中拿到子线程发来的结果参数AsyncTaskResult,AsyncTaskResult里面封装了AsyncTask对象和数据信息,如下:
?
1
2
3
4
5
6
7
8
private static class AsyncTaskResult<data> {
     final AsyncTask mTask;
     final Data[] mData;
     AsyncTaskResult(AsyncTask task, Data... data) {
         mTask = task;
         mData = data;
     }
}</data>
然后根据不同状态调用不同方法,如果是MESSAGE_POST_RESULT状态,就调用AsyncTask的finish()方法,finish方法中会去判断当前任务十分被cancel,如果没有cancel则开始回调onPostExecute()方法;如果状态是MESSAGE_POST_PROGRESS,则回调onProgressUpdate()方法。当子线程需要和UI线程进行通信时,就会通过这个handler,往UI线程发送消息。需要通过handler来发送消息,肯定是在子线程异步任务的时候才需要,在AsyncTask中需要handler的地方其实就是两个地方,一个是doInBackground()在运行过程中,需要更新进度值的时候;一个是doInBackground()运行完成后,需要回到到UI线程中的onPostExecute()方法的时候。 对于一:我们在doInBackground()中调用publicProgress()进行进度值的更新,因此在publicProgress()中肯定会有handler的身影,如下:
?
1
2
3
4
protected final void publishProgress(Progress... values) {
     if (!isCancelled()) {
         <em>getHandler</em>().obtainMessage(<em>MESSAGE_POST_PROGRESS</em>,
                 new AsyncTaskResult
(this, values)).sendToTarget();}} 对于二:其实就是一个异步任务执行完后后的返回处理,而FutureTask正是处理处理Runnable运行返回结果的。在2.1部分的executeOnExecutor方法中第四步,我们在执行execute(mFuture),传入了一个mFuture,mFuture是在初始化AsyncTask的时候进行构建的,如下:
?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mFuture = new FutureTask<result>(mWorker) {
     @Override
     protected void done() {
         try {
             postResultIfNotInvoked(get()); //这里面通过handler往UI线程发送消息
         } catch (InterruptedException e) {
             android.util.Log.<em>w</em>(<em>LOG_TAG</em>, e);
         } catch (ExecutionException e) {
             throw new RuntimeException( "An error occurred while executing doInBackground()" ,
                     e.getCause());
         } catch (CancellationException e) {
             postResultIfNotInvoked( null );
         }
     }
};</result>
在上面的postResultIfNotInvoked()中会通过handler进行消息的发送。  AsyncTask原理总结:AsyncTask主要是对异步任务和handler的封装,在处理异步任务时,AsyncTask内部使用了两个线程池,一个线程池sDefaultExecutor是用来处理用户提交(执行AsyncTask的execute时)过来的异步任务,这个线程池中有一个Runnable异步任务队列ArrayDequemTasks,把提交过来的异步任务放到这个队列中;另一个线程池THREAD_POOL_EXECUTOR,用来真正执行异步任务的。在处理handler时,自定义了一个InternalHandler,在publicProgress()和doInBackground()运行完成后,会通过这个handler往UI线程发送Message。 

3、AsyncTask在使用中的一个特殊情况

我们在使用AsyncTask的时候,一般会在onPreExecute()和onPostExecute()中进行UI的更新,比如等待图片的显示、进度条的显示...当我们一个Activity中正在使用AsyncTask进行文件的下载时,如果此时屏幕发生了旋转,Activity会进行re-onCreate,又会创建一个AsyncTask进行文件的下载,这个正是我们前面将取消任务的时候谈到的第二个现象,我们只需要在onPause()中进行取消cancel()即可。但是这样仅仅是解决了发生等待的情况,因为Activity再次进入了onCreate()方法,还是会进行文件的下载,为了解决这个问题,一种方案是通过判断onCreate(Bundle savedInstanceState)方法参数中的savedInstanceState==null?来判断是哪种情况,只有savedInstanceState==null时才去创建新的AsyncTask。 

4、AsyncTask和Handler的比较

AsyncTask:优点:AsyncTask是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制;缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。 Handler:优点:代码结构清晰,容易处理多个异步任务;缺点:当有多个异步任务时,由于要配合Thread或Runnable,代码可能会稍显冗余。 总之,AsyncTask不失为一个非常好用的异步任务处理类,只要不是频繁对大量UI进行更新,可以考虑使用;而Handler在处理大量UI更新时可以考虑使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值