Android中从源码分析关于AsyncTask的使用

http://blog.csdn.net/linmiansheng/article/details/20315823


Android在框架层提供了异步任务类,AysncTask,用于执行后台任务,并将执行结果更新到UI线程。为什么要用异步任务呢?这是因为如果某个任务太耗时间的话,会阻塞UI主线程,而我们知道UI线程如果阻塞5秒的话,就会发生ANR(Application No Response)错误 ,就算没有发生,主界面上看起来也会卡卡的,这显然用户体验就不太好了。

而AsyncTask类则在不影响UI线程的情况下,另起子线程去做那些耗时比较久的任务,比如我们在TodoList中去读取图片这种情况,然后将处理后的结果再更新到UI线程,从而达到更好的用户体验。

下面是AsyncTask类的定义:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public abstract class AsyncTask<Params, Progress, Result> {  
  2.     private static final String LOG_TAG = "AsyncTask";  
  3.     ...  

从上面的代码可以看出,AsyncTask是一个抽象类,所以在使用AsyncTask的时候,我们要继承它,实现一个子类,并实现其中一个方法,如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class LoadImageTask extends AsyncTask<String, Void, ImageView>{  
  2.   
  3.     @Override  
  4.     protected ImageView doInBackground(String... params) {  
  5.         String photoImagePath = params[0];  
  6.         Bitmap bitmap = BitmapReader.readBigBitmapFromFile(photoImagePath,REQ_WIDTH);  
  7.         ImageView imageView = Helper.createImageViewFromBitmap(DetailActivity.this, bitmap);  
  8.         return imageView;  
  9.     }  
  10.       
  11.     protected void onPostExecute(ImageView result) {  
  12.         imageViews.add(result);  
  13.         refreshGallery();  
  14.     }  
  15.       
  16. }  

对照 AsyncTask的三个参数,Params, Progress和Result,是泛型参数。

1)Params :参数,比如例子中定义的String类型,指向一个Image的路径,这是传给AysncTask的doInBackground方法。

2)Progress:这是在执行过程中会用到的一个参数,一般在显示进度的时候会用到,在例子中不会用到,所以可以在这里指定为Void。

3)Result:这是执行的结果对象,会作为onPostExecute方法的参数,在UI线程中处理。

由上面的例子也可以看到,一般我们实现的这个子类,要实现其中的两个方法

1)doInBackground:这个方法,其实是在另外一个线程中执行的。

2)onPostExecute:而这个方法却已经回到UI线程中来执行,所以可以在这里将上一个方法中执行所得的结果在这里更新到UI线程中。

那么这里面的机制是怎么样的呢,我们就来深入地看一下,也学习一下吧。

首先我们来看一下在Activity中,我们是怎么样来使用我们这个AsyncTask的吧,代码如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data){        
  3.     if (requestCode == REQUEST_FOR_CAMERA) {  
  4.         if(resultCode == RESULT_OK){  
  5.             isPhotoTaken = true;              
  6.             photoFileNames.add(tempPhotoFileName);            
  7.         }  
  8.     } else if (requestCode == REQUEST_FOR_GALLERY) {  
  9.         if(resultCode == RESULT_OK){  
  10.             isPhotoTaken = true;              
  11.             ContentResolver resolver = getContentResolver();  
  12.             Uri uri = data.getData();  
  13.             tempPhotoFileName = Helper.getImagePath(resolver, uri);  
  14.             photoFileNames.add(tempPhotoFileName);                
  15.         }  
  16.     }  
  17.     new LoadImageTask().execute(tempPhotoFileName);//调用其execute方法  
  18. }  

在TodoList的小demo中,在相机或者图库返回来图片的路径之后,我们就会调用AsynctTask的execute方法了,那么这里就是入口了。

进入AsyncTask类中,找到execute方法,如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public final AsyncTask<Params, Progress, Result> execute(Params... params) {  
  2.     return executeOnExecutor(sDefaultExecutor, params);  
  3. }  
可以看到,这里调用的是executeOnExecutor方法,我们继续找:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,  
  2.         Params... params) {  
  3.     if (mStatus != Status.PENDING) {  
  4.         switch (mStatus) {  
  5.             case RUNNING:  
  6.                 throw new IllegalStateException("Cannot execute task:"  
  7.                         + " the task is already running.");  
  8.             case FINISHED:  
  9.                 throw new IllegalStateException("Cannot execute task:"  
  10.                         + " the task has already been executed "  
  11.                         + "(a task can be executed only once)");  
  12.         }  
  13.     }  
  14.   
  15.     mStatus = Status.RUNNING;  
  16.   
  17.     onPreExecute();  
  18.   
  19.     mWorker.mParams = params;  
  20.     exec.execute(mFuture);  
  21.   
  22.     return this;  
  23. }  
在这个方法中,我们可以发现,AsynctTask定义了一个mStatus的成员变量,来表示异步任务的运行状态,分别是pending,running和finished,但只有处于pending状态的AsnycTask才能被执行。当状态是Pending的时候,就会继续执行,将状态变成running,这样能够保证AsyncTask只会被执行一次。

接着会调用onPreExecute方法做一些处理工作,这个方法其实我们也可以在子类中自己实现,如果有什么需要处理的话,一般不用。

然后我们发现,我们传进来的参数params会被传给mWorker,然后会有一个executor来执行execute的方法,并且执行的参数是一个mFuture对象。那么这两个东西是什么呢?

接着看如下代码:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public AsyncTask() {  
  2.     mWorker = new WorkerRunnable<Params, Result>() {  
  3.         public Result call() throws Exception {  
  4.             mTaskInvoked.set(true);  
  5.   
  6.             Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  7.             //noinspection unchecked  
  8.             return postResult(doInBackground(mParams));  
  9.         }  
  10.     };  
  11.   
  12.     mFuture = new FutureTask<Result>(mWorker) {  
  13.         @Override  
  14.         protected void done() {  
  15.             try {  
  16.                 postResultIfNotInvoked(get());  
  17.             } catch (InterruptedException e) {  
  18.                 android.util.Log.w(LOG_TAG, e);  
  19.             } catch (ExecutionException e) {  
  20.                 throw new RuntimeException("An error occured while executing doInBackground()",  
  21.                         e.getCause());  
  22.             } catch (CancellationException e) {  
  23.                 postResultIfNotInvoked(null);  
  24.             }  
  25.         }  
  26.     };  
  27. }  

mWorker是一个WorkerRunnable对象,而实际上WorkerRunnable是AsyncTask的一个抽象内部类,实现了Callable接口,如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  
  2.     Params[] mParams;  
  3. }  

而在上面的赋值过程,通过匿名类实现了WorkerRunnable,并其call方法中将线程优先级调整为后台线程,然后会执行 doInBackground方法,不过现在还没执行,咱们再看看FutureTask对象,下面是它的构造函数:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public FutureTask(Callable<V> callable) {  
  2.     if (callable == null)  
  3.         throw new NullPointerException();  
  4.     sync = new Sync(callable);  
  5. }  

而FutureTask是Java多线程模型的一部分,其间接地实现了Runnable(线程属性)接口和Future(异步任务)接口,而在上面的executeOnExecutor方法中,正是将mWorker(实现了Callable接口)对象传给它了,并被其封装到成员变量sync中,这样它们就共同表示了一个异步任务,到这里,一个异步任务也就创建完成了。

从上面executeOnExecutor的方法,也可以看到,真正执行execute是一个Executor,而在AsyncTask中,其实是SerialExecutor,我们看看其构造函数:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static class SerialExecutor implements Executor {  
  2.     final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  
  3.     Runnable mActive;  
  4.   
  5.     public synchronized void execute(final Runnable r) {  
  6.         mTasks.offer(new Runnable() {  
  7.             public void run() {  
  8.                 try {  
  9.                     r.run();//在新线程中调用了我们上面传进来的FutureTask的run方法  
  10.                 } finally {  
  11.                     scheduleNext();  
  12.                 }  
  13.             }  
  14.         });  
  15.         if (mActive == null) {  
  16.             scheduleNext();  
  17.         }  
  18.     }  
  19.   
  20.     protected synchronized void scheduleNext() {  
  21.         if ((mActive = mTasks.poll()) != null) {  
  22.             THREAD_POOL_EXECUTOR.execute(mActive);  
  23.         }  
  24.     }  
  25. }  

从上面,我们可以看到execute方法的参数就是一个Runnable,其实也就是FutrueTask了,在这里,它会被封装到一个新的线程中作为一个任务添加到mTasks队列中,至于下面如何在THREAD_POOL_EXECUTOR中去执行mTasks我们就不再深入下去了,我们就来看看FutureTask的run方法,如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. public void run() {  
  2.     sync.innerRun();  
  3. }  

我们发现,它调用的是sync.innerRun(),那么Sync的innerRun方法又干了什么呢?

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void innerRun() {  
  2.     if (!compareAndSetState(READY, RUNNING))  
  3.         return;  
  4.   
  5.     runner = Thread.currentThread();  
  6.     if (getState() == RUNNING) { // recheck after setting thread  
  7.         V result;  
  8.         try {  
  9.             result = callable.call();  
  10.         } catch (Throwable ex) {  
  11.             setException(ex);  
  12.             return;  
  13.         }  
  14.         set(result);  
  15.     } else {  
  16.         releaseShared(0); // cancel  
  17.     }  
  18. }  
我们发现,它调用了callable的call方法,啊,我们终于来到了mWorker的call方法,回过头看,不就是在那里调用了doInBackground方法的吗?所以,我们可以确定,doInBackground的确是在一个新的线程中执行的,并且是一个后台线程。

但同时我们发现,其执行完之后,是作为一个参数传递给postResult的,继续看下去:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private Result postResult(Result result) {  
  2.     @SuppressWarnings("unchecked")  
  3.     Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,  
  4.             new AsyncTaskResult<Result>(this, result));  
  5.     message.sendToTarget();  
  6.     return result;  
  7. }  
可以看到,在这里是利用了sHandler来发送异步消息的,而sHandler是在AsynctTask中定义的,如下:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static final InternalHandler sHandler = new InternalHandler();  
InternalHandler定义如下
[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private static class InternalHandler extends Handler {  
  2.     @SuppressWarnings({"unchecked""RawUseOfParameterizedType"})  
  3.     @Override  
  4.     public void handleMessage(Message msg) {  
  5.         AsyncTaskResult result = (AsyncTaskResult) msg.obj;  
  6.         switch (msg.what) {  
  7.             case MESSAGE_POST_RESULT:  
  8.                 // There is only one result  
  9.                 result.mTask.finish(result.mData[0]);  
  10.                 break;  
  11.             case MESSAGE_POST_PROGRESS:  
  12.                 result.mTask.onProgressUpdate(result.mData);  
  13.                 break;  
  14.         }  
  15.     }  
  16. }  

我们可以在这里发现,如果是MESSAGE_POST_RESULT,则会调用mTask的finish方法,然后就结束了,而如果是MESSAGE_POST_PROGRESS,则会调用 onProgressUpdate方法,那么,很显然这个mTask应该就是我们定久的AsyncTask了吧。我们看看AsyncTaskResult是什么先,

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. @SuppressWarnings({"RawUseOfParameterizedType"})  
  2. private static class AsyncTaskResult<Data> {  
  3.     final AsyncTask mTask;  
  4.     final Data[] mData;  
  5.   
  6.     AsyncTaskResult(AsyncTask task, Data... data) {  
  7.         mTask = task;  
  8.         mData = data;  
  9.     }  
  10. }  
我们发现,在上面传进来的AsyncTaskResult的参数task的正好就是this,而它又赋值给了mTask的,所以说明,这个mTask就是我们在Activity中定义的LoadImageTask了,而它是在主线程中定义的,那么很显然,它的finish方法也是在主线程,也就是UI线程中执行的了,再来看看这个finish方法:

[java]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. private void finish(Result result) {  
  2.     if (isCancelled()) {  
  3.         onCancelled(result);  
  4.     } else {  
  5.         onPostExecute(result);  
  6.     }  
  7.     mStatus = Status.FINISHED;  
  8. }  
在这里,我们终于看到了onPostExecute方法的调用了,这说明了,onPostExecute就是在UI线程中干活的了。最后mStatus也被设计成FIINISHED了,这个异步任务也就结束了。

嗯,关于AsyncTask的基本原理到这里也就结束了。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值