Android AsyncTask源码分析

Android AsyncTask源码分析,第1张

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

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,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 AsyncTask源码分析全部内容,希望文章能够帮你解决Android AsyncTask源码分析所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1149490.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存