首页 > 系统 > Android > 正文

详解Android中用于线程处理的AsyncTask类的用法及源码

2019-12-12 06:25:47
字体:
来源:转载
供稿:网友

为什么要用AsyncTask
我们写App都有一个原则,主线程不能够运行需要占用大量CPU时间片的任务,如大量复杂的浮点运算,较大的磁盘IO操作,网络socket等,这些都会导致我们的主线程对用户的响应变得迟钝,甚至ANR,这些会使应用的用户体验变差,但是有时又的确需要执行这些耗时的任务,那么我们通常可以使用AsyncTask或者new Thread
来处理,这样把任务放入工作线程中执行,不会占用主线程的时间片,所以主线程会及时响应用户的操作,如果使用new Thread来执行任务,那么如果需要中途取消任务执行或者需要返回任务执行结果,就需要我们自己维护很多额外的代码,而AsyncTask是基于concurrent架包提供的并发类实现的,上面的二个需求都已经帮我们封装了,这也是我们选择AsyncTask的原因。

怎么用AsyncTask
我们还是简单介绍下AsyncTask一些使用示例。我们先新建一个类DemoAsyncTask继承AsyncTask,因为AsyncTask是抽象类,其中doInBackground方法必须重写。

private class DemoAsyncTask extends AsyncTask<String, Void, Void> { @Override protected void onPreExecute() {  super.onPreExecute(); } @Override protected Void doInBackground(String... params) {  return null; }  @Override protected void onPostExecute(Void aVoid) {  super.onPostExecute(aVoid); } @Override protected void onProgressUpdate(Void... values) {  super.onProgressUpdate(values); } @Override protected void onCancelled(Void aVoid) {  super.onCancelled(aVoid); } @Override protected void onCancelled() {  super.onCancelled(); }}DemoAsyncTask task = new DemoAsyncTask();task.execute("demo test AsyncTask");//task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, "test");//myTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "test");

简单分析下
上面就是AsyncTask最简单的使用方法,我们上面重写的方法中,onInBackground方法运行在工作线程,其他的方法全部运行在主线程,另外它的运行方式Android提供给我们2个方法,上面都列出了。

1.第一个方法会使用默认的Executor执行我们的任务, 其实也就是SERIAL_EXECUTOR,SERIAL_EXECUTOR我们其实也是可以通过方法去自定义的,Android帮我们的默认实现是逐个执行任务,也就是单线程的,关于AsyncTask的任务执行是单线程实现还是多线程实现还有一段很有意思的历史,较早的版本是单线程实现,从Android2.X开始,Google又把它改为多线程实现,后来Google发现,多线程实现的话,会有很多需要保证线程安全的额外工作留给开发者,所以从Android3.0开始,又把默认实现改为单线程了,今天我们演示的是Framwork代码版本是21(Android5.0)。
2.同时也提供了多线程实现的接口,即我们上面写的最后一行代码 AsyncTask.THREAD_POOL_EXECUTOR。

public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

其实相信大家平时工作学习中经常使用AsyncTask,我们直接去看AsyncTask类源码(插一句题外话,平时大家也可以把自己工作学习中的心得体会总结一下,记下来~~)

public abstract class AsyncTask<Params, Progress, Result> {....}

AsyncTask抽象类,有三个泛型参数类型,第一个是你需要传递进来的参数类型,第二个是任务完成进度的类型一般是Integer,第三个是任务完成结果的返回类型,有时这些参数不是全部需要,不需要的设为Void即可,另外Result只有一个,但Params可以有多个。
我们可以看到AsyncTask的默认构造器初始化了二个对象,mWorker和mFuture。

private final WorkerRunnable<Params, Result> mWorker;private final FutureTask<Result> mFuture; mWorker = new WorkerRunnable<Params, Result>() {  public Result call() throws Exception {   mTaskInvoked.set(true);   Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);   //noinspection unchecked   return postResult(doInBackground(mParams));  } }; mFuture = new FutureTask<Result>(mWorker) {  @Override  protected void done() {   try {    postResultIfNotInvoked(get());   } catch (InterruptedException e) {    android.util.Log.w(LOG_TAG, e);   } catch (ExecutionException e) {    throw new RuntimeException("An error occured while executing doInBackground()",      e.getCause());   } catch (CancellationException e) {    postResultIfNotInvoked(null);   }  } };

mWoker实现了Callback接口,Callback接口是JDK1.5加入的高级并发架包里面的一个接口,它可以有一个泛型返回值。

public interface Callable<V> {/** * Computes a result, or throws an exception if unable to do so. * * @return computed result * @throws Exception if unable to compute a result */V call() throws Exception;}

FutureTask实现了RunnableFuture接口,RunnableFuture接口是JDK提供的,看名称就知道它继承了Runnable和Future接口,mFuture是FutureTask的一个实例,可以直接被Executor类实例execute。我们来继续看AsyncTask的execute方法。

public final AsyncTask<Params, Progress, Result>  execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,  Params... params) { if (mStatus != Status.PENDING) {  switch (mStatus) {   case RUNNING:    throw new IllegalStateException("Cannot execute task:"      + " the task is already running.");   case FINISHED:    throw new IllegalStateException("Cannot execute task:"      + " the task has already been executed "      + "(a task can be executed only once)");  } } mStatus = Status.RUNNING; onPreExecute(); mWorker.mParams = params; exec.execute(mFuture); return this;}

先调用onPreExecute()方法,此时还在主线程(严格来说是调用AsyncTask执行的线程),然后exec.execute(mFuture),把任务交给exec处理,execute mFuture其实就是invoke mWoker,然后调用postResult(doInBackground(mParams)),此时已经运行在工作线程池,不会阻塞主线程。然后给mHandler发送MESSAGE_POST_RESULT消息,然后调用finish方法,如果isCancelled,回调onCancelled,否则回调onPostExecute。

private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,   new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result;}private static final InternalHandler sHandler = new InternalHandler();private static class InternalHandler extends Handler { @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) @Override public void handleMessage(Message msg) {  AsyncTaskResult result = (AsyncTaskResult) msg.obj;  switch (msg.what) {   case MESSAGE_POST_RESULT:    // There is only one result    result.mTask.finish(result.mData[0]);    break;   case MESSAGE_POST_PROGRESS:    result.mTask.onProgressUpdate(result.mData);    break;  } }}private void finish(Result result) { if (isCancelled()) {  onCancelled(result); } else {  onPostExecute(result); } mStatus = Status.FINISHED;}

现在其实我们已经把AsyncTask整个执行任务的过程走完了,其中暴露给我们的那几个回调方法也都走到了。现在我们回过头来看,AsyncTask其实只是对JDK 1.5提供的高级并发特性,concurrent架包做的一个封装,方便开发者来处理异步任务,当然里面还有很多细节处理的方法值得大家学习,如任务执行进度的反馈,任务执行原子性的保证等,这些留给大家自己学习了。

源码分析
下面我们再深入一些,来看AsyncTask的源码。下面分析这个类的实现,主要有线程池以及Handler两部分。

线程池
当执行一个AsyncTask的时候调用的是execute()方法,就从这个开始看:

public final AsyncTask<Params, Progress, Result> execute(Params... params){ return executeOnExecutor(sDefaultExecutor, params);}public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,   Params... params) {  if (mStatus != Status.PENDING) {   switch (mStatus) {    case RUNNING:     throw new IllegalStateException("Cannot execute task:" + " the task is already running.");           case FINISHED:     throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " + "(a task can be executed only once)");               }  }   mStatus = Status.RUNNING;  //先执行 onPreExecute onPreExecute();   mWorker.mParams = params;   exec.execute(mFuture);  return this; } 

execute方法会调用executeOnExecutor。在这个方法中先检查任务是否已经执行或者执行结束,然后把任务标记为running。最开始执行的是onPreExecute,接着把参数赋值给mWorker对象。这个mWorker是一个Callable对象,最终被包装为FutureTask,代码如下:

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {  Params[] mParams; } mWorker = new WorkerRunnable<Params, Result>() {   public Result call() throws Exception {    mTaskInvoked.set(true);    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);    //noinspection unchecked    return postResult(doInBackground(mParams));   }  }; mFuture = new FutureTask<Result>(mWorker) {  @Override  protected void done() {   try {    postResultIfNotInvoked(get());   } catch (InterruptedException e) {    android.util.Log.w(LOG_TAG, e);   } catch (ExecutionException e) {    throw new RuntimeException("An error occured while executing doInBackground()",      e.getCause());   } catch (CancellationException e) {    postResultIfNotInvoked(null);   }  } }; 

   
从上面的代码可以看出,在mWorker对象中的call()方法会调用doInbackground,返回值交给postResult方法,这个方法通过Handler发送消息,这一点稍后再详细分析。

在mWorker对象被封装成FutureTask之后交由线程池执行,从execute方法可以看出,使用的是sDefaultExecutor,它的值默认为SERIAL_EXECUTOR,也就是串行执行器,实现如下:

 

private static class SerialExecutor implements Executor {  //线性双向队列,用来存储所有的AsyncTask任务  final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();  //当前正在执行的AsyncTask任务  Runnable mActive;  public synchronized void execute(final Runnable r) {   //将新的AsyncTask任务加入到双向队列中   mTasks.offer(new Runnable() {    public void run() {     try {      //执行AsyncTask任务      r.run();     } finally {      //当前任务执行结束后执行下一个任务     scheduleNext();     }    }   });   if (mActive == null) {    scheduleNext();   }  }  protected synchronized void scheduleNext() {   //从任务队列中取出队列头部的任务,如果有就交给并发线程池去执行   if ((mActive = mTasks.poll()) != null) {    THREAD_POOL_EXECUTOR.execute(mActive);   }  } }public static final Executor THREAD_POOL_EXECUTOR   = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,     TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 

在上面的代码中,如果有任务执行,那么SerialExecutor的execute方法会被调用,它的逻辑是把Runnable对象加入ArrayDeque队列中,然后判断mActivie是否为空。第一次执行时mActive当然为空,所以执行scheduleNext,其实就是取出任务队列中的第一个任务交给线程池(THREAD_POOL_EXECUTOR)执行。加入mTask队列的Runnable对象的run方法里最终一定会调用scheduleNext,那么又会从任务队列中取出队头任务执行。这样便实现了单线程顺序执行任务,所以在AsyncTask中默认启用的是单线程执行,只有上一个任务执行后才会执行下一个任务。如果想要启用多线程执行任务,可以直接调用 executeOnExecutor(Executor exec, Params... params),这里的Executor参数可以使用AsyncTask自带的THREAD_POOL_EXECUTOR,也可以自己定义。

Handler
AsyncTask内部用Handler传递消息,它的实现如下:

private static class InternalHandler extends Handler {  @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})  @Override  public void handleMessage(Message msg) {   AsyncTaskResult result = (AsyncTaskResult) msg.obj;   switch (msg.what) {    case MESSAGE_POST_RESULT:     // There is only one result     result.mTask.finish(result.mData[0]);     break;    case MESSAGE_POST_PROGRESS:     result.mTask.onProgressUpdate(result.mData);     break;   }  } } 

如果消息类型是任务执行后的返回值(MESSAGE_POST_RESULT)将调用finish()方法:

private void finish(Result result) {  if (isCancelled()) {   onCancelled(result);  } else {   onPostExecute(result);  }  mStatus = Status.FINISHED; } 

从上面可以知道,如果任务取消了,将调用onCancelled,否则调用onPostExecute,所以一个AsyncTask任务如果取消了,那么onPostExecute将不会得到执行。

如果消息类型是执行进度(MESSAGE_POST_PROGRESS)将调用onProgressUpdate,这个方法默认是空方法,我们可以根据自己的需要重写。

总结
AsyncTask的主要逻辑就如上面所分析的,总结几个需要注意的地方:

  • AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)
  • AsyncTask对象必须在UI线程创建
  • execute方法必须在UI线程调用
  • 不要手动调用onPreExecute()、doInBackground、onProgressUpdate方法
  • 一个任务只能被调用一次(第二次调用会抛出异常)
发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表