Android 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, 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完全解析,带你从源码的角度彻底理解

时间: 2016-04-02

Android中使用AsyncTask做下载进度条实例代码

android AsyncTask做下载进度条 AsyncTask是个不错的东西,可以使用它来做下载进度条.代码讲解如下: package com.example.downloadfile; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; import android.a

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

我们都知道,Android UI是线程不安全的,如果想要在子线程里进行UI操作,就需要借助Android的异步消息处理机制.之前我也写过了一篇文章从源码层面分析了Android的异步消息处理机制. 不过为了更加方便我们在子线程中更新UI元素,Android从1.5版本就引入了一个AsyncTask类,使用它就可以非常灵活方便地从子线程切换到UI线程,我们本篇文章的主角也就正是它了. AsyncTask很早就出现在Android的API里了,所以我相信大多数朋友对它的用法都已经非常熟悉.不过今天我

Android屏幕旋转 处理Activity与AsyncTask的最佳解决方案

一.概述 运行时变更就是设备在运行时发生变化(例如屏幕旋转.键盘可用性及语言).发生这些变化,Android会重启Activity,这时就需要保存activity的状态及与activity相关的任务,以便恢复activity的状态. 为此,google提供了三种解决方案: 对于少量数据: 通过onSaveInstanceState(),保存有关应用状态的数据. 然后在 onCreate() 或 onRestoreInstanceState()期间恢复 Activity 状态. 对于大量数据:用

Android AsyncTask实现机制详细介绍及实例代码

Android AsyncTask实现机制 示例代码: public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Pa

Android中使用AsyncTask实现文件下载以及进度更新提示

Android提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单.相对Handler来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handter即可实现.AsyncTask是抽象类.AsyncTask定义了三种泛型类型Params,Progress和Result: Params启动任务执行的输入参数,比如,HTTP请求的URL. Progress后台任务执行的百分比. Result后台执行任务最终返回的结果,比如String.

Android AsyncTask用法巧用实例代码

Android AsyncTask 联系人导入 new AsyncTask<ArrayList<ContactInfo>, Integer, ArrayList<ContactInfo>>() { @Override protected void onPreExecute() { super.onPreExecute(); if (importingDialog != null) importingDialog.show(); } @Override protected

Android带进度条的文件上传示例(使用AsyncTask异步任务)

最近项目中要做一个带进度条的上传文件的功能,学习了AsyncTask,使用起来比较方便,将几个方法实现就行,另外做了一个很简单的demo,希望能对大家有帮助,在程序中设好文件路径和服务器IP即可. demo运行截图: AsyncTask是抽象类,子类必须实现抽象方法doInBackground(Params... p),在此方法中实现任务的执行工作,比如联网下载或上传.AsyncTask定义了三种泛型类型Params,Progress和Result. 1.Params 启动任务执行的输入参数,比

详解Android App中的AsyncTask异步任务执行方式

基本概念 AsyncTask:异步任务,从字面上来说,就是在我们的UI主线程运行的时候,异步的完成一些操作.AsyncTask允许我们的执行一个异步的任务在后台.我们可以将耗时的操作放在异步任务当中来执行,并随时将任务执行的结果返回给我们的UI线程来更新我们的UI控件.通过AsyncTask我们可以轻松的解决多线程之间的通信问题. 怎么来理解AsyncTask呢?通俗一点来说,AsyncTask就相当于Android给我们提供了一个多线程编程的一个框架,其介于Thread和Handler之间,我

Android中通过AsyncTask类来制作炫酷进度条的实例教程

AsyncTask (API level 3,所以几乎所有目前在市面上流通的 Android 版本皆可使用) 是除 Thread 外的另一种选择,Android 团队鼓励主执行绪(UI thread) 专注于操作 & 画面的流畅呈现, 其余工作 (如网络资料传输.档案/磁碟/资料存取) 最好都在背景执行: Thread 通常要搭配 Handler 使用,而 AsyncTask 用意在简化背景执行 thread 程序码的撰写. 如果您预期要执行的工作能在几秒内完成,就可以选择使用 AsyncTas

Android中使用AsyncTask实现下载文件动态更新进度条功能

1. 泛型 AysncTask<Params, Progress, Result> Params:启动任务时传入的参数,通过调用asyncTask.execute(param)方法传入. Progress:后台任务执行的进度,若不用显示进度条,则不需要指定. Result:后台任务结束时返回的结果. 2. 重要方法 doInBackground(Params... params):必须重写的方法,后台任务就在这里执行,会开启一个新的线程.params为启动任务时传入的参数,参数个数不定. on

Android自定义View实现炫酷进度条

本文实例为大家分享了Android实现炫酷进度条的具体代码,供大家参考,具体内容如下 下面我们来实现如下效果: 第一步:创建attrs文件夹,自定义属性: <?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyProgress"> <attr name="out_color" form

Android打造炫酷进度条效果

本文实例为大家分享了Android炫酷进度条效果的具体代码,供大家参考,具体内容如下 学习:视频地址 HorizontalProgressbarWithProgress的代码 import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.os.Build; imp

Android 中通过实现线程更新Progressdialog (对话进度条)

作为开发者我们需要经常站在用户角度考虑问题,比如在应用商城下载软件时,当用户点击下载按钮,则会有下载进度提示页面出现,现在我们通过线程休眠的方式模拟下载进度更新的演示,如图(这里为了截图方便设置对话进度条位于屏幕上方): layout界面代码(仅部署一个按钮): <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.androi

Android中使用BitmapShader类来制作各种图片的圆角

public   BitmapShader(Bitmap bitmap,Shader.TileMode tileX,Shader.TileMode tileY) 调用这个类来产生一个画有一个位图的渲染器(Shader). bitmap:在渲染器内使用的位图 (1)tileX:The tiling mode for x to draw the bitmap in.   在位图上X方向花砖模式 (2)tileY:The tiling mode for y to draw the bitmap in.

简介Android 中的AsyncTask

AsyncTask是一个很常用的API,尤其异步处理数据并将数据应用到视图的操作场合.其实AsyncTask并不是那么好,甚至有些糟糕.本文我会讲AsyncTask会引起哪些问题,如何修复这些问题,并且关于AsyncTask的一些替代方案. AsyncTask 从Android API 3(1.5 Cupcake)开始,AsyncTask被引入用来帮助开发者更简单地管理线程.实际上在Android 1.0和1.1也是有类似的实现,那就是UserTask.UserTask和AsyncTask有着相

Android 中使用 AsyncTask 异步读取网络图片

 1.新建Android工程AsyncLoadPicture 新建布局文件activity_main.xml主界面为一个GridView,还有其子项布局文件gridview_item.xml 2.功能主界面MainActivity.java,主代码如下 package com.example.asyncloadpicture; import java.util.ArrayList; import android.app.Activity; import android.content.Conte

Android中使用TabHost 与 Fragment 制作页面切换效果

三个标签页置于顶端 效果图: 在文件BoardTabHost.java中定义页面切换的效果:切换页面时,当前页面滑出,目标页面滑入.这是2个不同的动画设定动画时要区分对待 import android.content.Context; import android.util.AttributeSet; import android.view.animation.Animation; import android.view.animation.TranslateAnimation; import

使用 Lambda 取代 Android 中的匿名类

Lambda是第十一个希腊字母,大写Λ,小写λ,额,跑题了-Lambda表达式 是Java8的新特性之一: Lambda表达式 函数式接口 流API 默认方法 新的Date Time API Lambda表达式 取代了匿名类 ,取消了模板,允许用函数式风格编写代码. 由于最近接触了RxJava,遇到了Lambda,立马就喜欢上了~所以就学习了一下. 本文主要介绍一下Lambda在Android中替代匿名类的部分使用场景. 在Android中使用Lambda gradle-retrolambda