Android AsyncTask源码分析

 更新时间:2016年04月05日 14:25:32   作者:然则  
这篇文章主要针对Android AsyncTask源码为大家进行分析,非常方便的AsyncTask类内部封装了Handler和线程池,感兴趣的小伙伴们可以参考一下

Android中只能在主线程中进行UI操作,如果是其它子线程,需要借助异步消息处理机制Handler。除此之外,还有个非常方便的AsyncTask类,这个类内部封装了Handler和线程池。本文先简要介绍AsyncTask的用法,然后分析具体实现。

基本用法
AsyncTask是一个抽象类,我们需要创建子类去继承它,并且重写一些方法。AsyncTask接受三个泛型参数:

Params: 指定传给任务执行时的参数的类型
Progress: 指定后台任务执行时将任务进度返回给UI线程的参数类型
Result: 指定任务完成后返回的结果的类型
除了指定泛型参数,还需要根据需要重写一些方法,常用的如下:

onPreExecute(): 这个方法在UI线程调用,用于在任务执行前做一些初始化操作,如在界面上显示加载进度控件
doInBackground: 在onPreExecute()结束之后立刻在后台线程调用,用于耗时操作。在这个方法中可调用publishProgress方法返回任务的执行进度
onProgressUpdate: 在doInBackground调用publishProgress后被调用,工作在UI线程
onPostExecute: 后台任务结束后被调用,工作在UI线程
源码分析
下面分析这个类的实现,主要有线程池以及Handler两部分。

1、线程池
当执行一个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,也可以自己定义。

2、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的主要逻辑就如上面所分析的,总结几个需要注意的地方:

      1)、 AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)
      2)、  AsyncTask对象必须在UI线程创建
      3)、  execute方法必须在UI线程调用
      4)、  不要手动调用onPreExecute()、doInBackground、onProgressUpdate方法
      5)、  一个任务只能被调用一次(第二次调用会抛出异常)

其它还有一些细节可以自行研究源码,另外推荐几篇不错的文章:

Android AsyncTask完全解析,带你从源码的角度彻底理解

相关文章

  • Android studio制作简易计算器功能

    Android studio制作简易计算器功能

    这篇文章主要为大家详细介绍了Android studio制作简易计算器功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • Android ContentProvider查看/读取手机联系人实例

    Android ContentProvider查看/读取手机联系人实例

    本篇文章主要介绍了Android ContentProvider查看/读取手机联系人实例,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • Android Application的使用全面解析

    Android Application的使用全面解析

    这篇文章主要为大家介绍了Android Application的使用全面解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • Android序列化之Parcelable和Serializable的使用详解

    Android序列化之Parcelable和Serializable的使用详解

    本篇文章主要介绍了Android序列化之Parcelable和Serializable的使用详解,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-01-01
  • Kotlin协程Dispatchers原理示例详解

    Kotlin协程Dispatchers原理示例详解

    这篇文章主要为大家介绍了Kotlin协程Dispatchers原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Android布局控件View ViewRootImpl WindowManagerService关系

    Android布局控件View ViewRootImpl WindowManagerService关系

    这篇文章主要为大家介绍了Android布局控件View ViewRootImpl WindowManagerService关系示例解析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • Android解析json数组对象的方法及Apply和数组的三个技巧

    Android解析json数组对象的方法及Apply和数组的三个技巧

    这篇文章主要介绍了Android解析json数组对象的方法及Apply和数组的三个技巧的相关资料,需要的朋友可以参考下
    2015-12-12
  • Android自定义view实现侧滑栏详解

    Android自定义view实现侧滑栏详解

    之前一直没有写侧滑菜单的实现方法,今天计划补上。手机开发中,往往存在很多功能没处放的问题。我们可能会把功能放入一个菜单列表,但现在一种流行的做法是侧滑菜单
    2022-11-11
  • Android自定义控件仿iOS滑块SwitchButton

    Android自定义控件仿iOS滑块SwitchButton

    这篇文章主要为大家详细介绍了Android自定义控件模仿iOS滑块SwitchButton,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-12-12
  • Android MediaPlayer音频播放器封装示例浅析

    Android MediaPlayer音频播放器封装示例浅析

    Android提供了许多方法来控制播放的音频/视频文件和流。其中该方法是通过一类称为MediaPlayer。Android是提供MediaPlayer类访问内置的媒体播放器的服务,如播放音频,视频等为了使用MediaPlayer,我们要调用这个类的静态create()方法
    2023-04-04

最新评论