在Android中,主线程是UI线程,当需要根据其他数据进行更新UI时,如果获取数据的操作比较耗时的话,会触发ANR,所以我们应该讲耗时的操作进行异步操作,尤其是请求网络数据的操作应该放在后台线程进行,避免ANR。

而AsyncTask是Android里很常用的异步任务请求方法,AsyncTaks基本用法都会用,网上也要好多教程,就不写例子了。

以前用的时候分析过源码,发现学问还是挺多的,今天总结一下,错误之处的请诸位指正。

 分析一个AsyncTask的源码,需要了解的知识:线程池,future模式,无锁操作,handler、looper和Message之间的关系。

1.先看一下AsyncTask的基本成员们:

public abstract class AsyncTask<Params, Progress, Result> {
private static final String LOG_TAG = "AsyncTask"; private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) {
return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
}
}; private static final BlockingQueue<Runnable> sPoolWorkQueue =
new LinkedBlockingQueue<Runnable>(128); /**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); /**
* An {@link Executor} that executes tasks one at a time in serial
* order. This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2; private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static InternalHandler sHandler; private final WorkerRunnable<Params, Result> mWorker;
private final FutureTask<Result> mFuture; private volatile Status mStatus = Status.PENDING; private final AtomicBoolean mCancelled = new AtomicBoolean();
private final AtomicBoolean mTaskInvoked = new AtomicBoolean();
//后续省略
}

首先我们看到了AsyncTask必备的3大参数Params,Progress,Result。后续我们会再见到它们。

CPU_COUNT是java虚拟机可用的处理器个数,可以通过Runtime类的availableProcessors()方法得到。

看到刚开始的几个静态常量CORE_POOL_SIZE和MAXMUM_POOL_SIZE,就知道AsyncTask实现了一个线程池的机制。

定义了一个线程工场,以便于自定义线程

任务阻塞队列是一个定义了一个大小为128的无界任务队列。

ThreadPoolExecutor定义了一个线程池,核心池大小是CORE_POOL_SIZE,线程池最大容量是MAXIMUM_POOL_SIZE,多余空闲线程的存活时间是KEEP_ALIVE,单位是秒,使用一个大小为128的LinkedBlockingQueue作为任务队列,用一个无锁整型计数的线程工场产生线程。
定义了两个常量MESSAGE_POST_RESULT和MESSAGE_POST_PROGRESS,用于与主线程通信的标签,表示当获取到与主线程交互的数据类型,是进度信息还是结果信息。

定义了两个无锁布尔值作为任务激活或者取消的标志位,无锁操作即CAS,使用于并发情况下数据的更新。

然后看一下这里自定义的SeiralExcuter,实现了Executor接口,用于串行执行任务,并且设置为默认的Executor.

AsyncTask定义了两个线程池:异步线程池THREAD_POOL_EXECUTOR,同步线程池SeiralExcuter

随着Android版本的不同,AsyncTask处理任务默认是串行还是并行几经更迭,至今又改回串行,因为如果有一个UI的状态信息需要不同任务下反复更改,那么并行处理显然会出现更新顺序的问题,很麻烦,所以尽量使用串行的方法。

2.执行任务的方式。

一个自定义WorkerRunnable,实现了Callable接口

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}

再看一下AsyncTask的初始化方法:

public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
}; 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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}

随后用这个WorkerRunnable实例构造了一个FutureTask实例,这个FutureTask则是JDK中对于future模式的一种实现。future模式是多线程开发中常用的一种设计模式,主要思想是异步调用。当Client想Server请求数据时,如果这个请求操作耗时比较长,那么Server会先立即返回一个FutureData,这个数据是一份虚拟的数据,此时Client就可以去做其他的事情了,过一段时间在调用get方法,如果这个时候Server已经准备好返回数据了,就会返回我们真正需要的数据RealData,否则Client会进入等待状态,直至返回RealData。简单的说,可以把FutureData理解成一份契约,可以用来装配RealData的契约。

这样Client就不用一直等待数据的返回,拿到FutureData后就可以先去做别的事情了,到需要的时候调用get方法获得RealData.

实现一个Callable接口,它的call方法会构造我们需要的真实数据并返回。

构造futuretask的时候,使用Callable接口,告诉futuretask我们需要的数据应该如何产生,使用future.get()来获取实际的数据。

在重复一遍,WorkerRunnable实现了Callable接口,然后用这个实例去构造一个FuntureTask实例。

当准备好future模式后,我们需要决定让任务串行执行还是并行执行,Android官方认为大多数情况下尽量选择串行执行,直接调用execute方法即默认使用SeiralExcuter。

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params);
}

如果一定要并行执行,则调用executeOnExecutor方法,并且设置Executor为前文定义的线程池THREAD_POOL_EXECTOR:

execute方法也是调用executeOnExecutor方法,只是将executor直接设置为默认的SeiralExecutor。

再强调一遍,必须在UI线程中调用执行方法。

3.执行的过程

直接看一下executeOnExector方法:

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;
}

可以看到,在执行前先检查任务状态,如果正在执行或者已经完成,则抛出异常。处于挂起状态则设置为RUNNING状态,随后执行onPreExecute方法,此方法需要重写,用于执行任务前对UI的动作。

随后将执行任务所需参数赋给我们自定义的WorkerRunnable实例mWorker构造future实例mFuture,执行futureTask,处理任务。最后返回this,是为了保持UI线程对这个AsyncTask的引用。

到这里,我们回头看一下AsyncTask初始化的方法了。 (上文已引用,此处隐藏)

public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
Result result = doInBackground(mParams);
Binder.flushPendingCommands();
return postResult(result);
}
}; 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 occurred while executing doInBackground()",
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}

WorkerRunnable中的Call方法是继承自Callable的方法,这个方法会返回需要构造的实际数据。任务线程被激活后就将其优先级设置为后台线程,将我们任务需要的Param参数传递给doInBackground方法,去在后台执行耗时任务,整个耗时任务包括网络请求和信息处理的过程,在执行该方法的时候,可以激活publishProcess方法来进行主线程进度更新的显示,当处理完数据后返回Result。

在mFuture中重写了done方法,即当mFuture的mWorker处理完数据后,mFuture调用了get方法和postResultIfNotInvoked方法,get方法如下:

public final Result get(long timeout, TimeUnit unit) throws InterruptedException,
ExecutionException, TimeoutException {
return mFuture.get(timeout, unit);
}

调用future模式的get方法,获取到了我们想要的结果数据,之后通过postResultIfNotInvoked方法,将数据发送出去。

private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
} private Result postResult(Result result) {
@SuppressWarnings("unchecked")
Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult<Result>(this, result));
message.sendToTarget();
return result;
}

终于到这一步了,可以看到数据处理完毕后,我们把在FutureTask中处理后的数据最终传给了一个message,用来与UI线程进行通信,有了Message,Handler还远么?通过getHandler方法得到一个Handler,这个Handler是自定义的InternalHandler

private static class InternalHandler extends Handler {
public InternalHandler() {
super(Looper.getMainLooper());
}
@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;
}

此处Looper.getMainLooper () 获得UI线程的Lopper,准备更新主线程的UI啦!

根据请求的标签信息判定所需行为,当需要更新进度信息时,将数据分发给onProgressUpdate方法去主线程显示进度的更新,当需要传递结果时调用finish方法,此时如果请求已被取消则不发送,如果没被取消则发送至UI线程进行UI信息的更新。

至此,AsyncTask的基本流程就走了一遍了。

总结一下:

利用FutureTask进行异步请求,通常情况下串行处理请求,如果有并行需求则使用线程池应对并发请求,线程池的配置取决于当前虚拟机所能使用的CPU数量。

三大参数:Params任务后台执行所需的参数类型,Progress是如果需要前台进度更新的更新值类型,Result则是任务返回值的类型。

使用execute方法激发异步任务执行,在发出请求之前,使用onPreExecute可以在执行任务前对UI进行标记,随后调用了doInBackground方法,进行后台耗时任务的操作,在这个过程中可以调用publishProgress方法将信息放入message,通过InternalHandler传递,调用onProgressUpdate方法将进度更新信息发送至UI线程,展示进度情况,处理完毕后,InternalHandler将结果数据的message通过onPostExecute发送至UI线程,UI线程根据结果数据进行UI的更新。

所以,在使用AsyncTask时,我们需要重写上面标红的四个方法。最少得重写一个doInBackground方法,一般情况下还得重写一个onPostExecute方法。

这种大的流程框架已经被写好(注意executrOnExecutor方法是final的),我么只需要自己定制流程一部分具体实现的方式,也就是设计模式之中的模板方法模式。

public class MainActivity extends Activity {

    private static final String TAG = "ASYNC_TASK";

    private Button execute;
private Button cancel;
private ProgressBar progressBar;
private TextView textView; private MyTask mTask; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main); execute = (Button) findViewById(R.id.execute);
execute.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常
mTask = new MyTask();
mTask.execute("http://www.baidu.com"); execute.setEnabled(false);
cancel.setEnabled(true);
}
});
cancel = (Button) findViewById(R.id.cancel);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取消一个正在执行的任务,onCancelled方法将会被调用
mTask.cancel(true);
}
});
progressBar = (ProgressBar) findViewById(R.id.progress_bar);
textView = (TextView) findViewById(R.id.text_view); } private class MyTask extends AsyncTask<String, Integer, String> {
//onPreExecute方法用于在执行后台任务前做一些UI操作
@Override
protected void onPreExecute() {
Log.i(TAG, "onPreExecute() called");
textView.setText("loading...");
} //doInBackground方法内部执行后台任务,不可在此方法内修改UI
@Override
protected String doInBackground(String... params) {
Log.i(TAG, "doInBackground(Params... params) called");
try {
HttpClient client = new DefaultHttpClient();
HttpGet get = new HttpGet(params[0]);
HttpResponse response = client.execute(get);
if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
HttpEntity entity = response.getEntity();
InputStream is = entity.getContent();
long total = entity.getContentLength();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buf = new byte[1024];
int count = 0;
int length = -1;
while ((length = is.read(buf)) != -1) {
baos.write(buf, 0, length);
count += length;
//调用publishProgress公布进度,最后onProgressUpdate方法将被执行
publishProgress((int) ((count / (float) total) * 100));
//为了演示进度,休眠500毫秒
Thread.sleep(500);
}
return new String(baos.toByteArray(), "gb2312");
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
return null;
} //onProgressUpdate方法用于更新进度信息
@Override
protected void onProgressUpdate(Integer... progresses) {
Log.i(TAG, "onProgressUpdate(Progress... progresses) called");
progressBar.setProgress(progresses[0]);
textView.setText("loading..." + progresses[0] + "%");
} //onPostExecute方法用于在执行完后台任务后更新UI,显示结果
@Override
protected void onPostExecute(String result) {
Log.i(TAG, "onPostExecute(Result result) called");
textView.setText(result); execute.setEnabled(true);
cancel.setEnabled(false);
} //onCancelled方法用于在取消执行中的任务时更改UI
@Override
protected void onCancelled() {
Log.i(TAG, "onCancelled() called");
textView.setText("cancelled");
progressBar.setProgress(0); execute.setEnabled(true);
cancel.setEnabled(false);
}
}
}

源码地址:https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/os/AsyncTask.java

AsyncTask源码分析的更多相关文章

  1. 【转载】AsyncTask源码分析

    原文地址:https://github.com/white37/AndroidSdkSourceAnalysis/blob/master/article/AsyncTask%E5%92%8CAsync ...

  2. 小白挑战:AsyncTask源码分析

    //AsyncTask从本质上讲,是对ThreadPool和handler的封装. 在学习线程池相关的知识时,看到书中提到AsyncTask的实现中使用到了ThreadPool,于是把源码翻了出来, ...

  3. 我的Android进阶之旅------>Android中AsyncTask源码分析

    在我的<我的Android进阶之旅------>android异步加载图片显示,并且对图片进行缓存实例>文章中,先后使用了Handler和AsyncTask两种方式实现异步任务机制. ...

  4. Android应用AsyncTask处理机制详解及源码分析

    1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机 ...

  5. 【转载】Android应用AsyncTask处理机制详解及源码分析

    [工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个 ...

  6. [转]【安卓笔记】AsyncTask源码剖析

    [转][安卓笔记]AsyncTask源码剖析 http://blog.csdn.net/chdjj/article/details/39122547 前言: 初学AsyncTask时,就想研究下它的实 ...

  7. Android Asynctask与Handler的比较,优缺点区别,Asynctask源码

    1  AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度(可以 ...

  8. AsyncTask源码解读

    AsyncTask源码解读 一.基本使用 protected void onPreExecute() protected abstract Result doInBackground(Params.. ...

  9. android高级---->AsyncTask的源码分析

    在Android中实现异步任务机制有两种方式,Handler和AsyncTask,它在子线程更新UI的例子可以参见我的博客(android基础---->子线程更新UI).今天我们通过一个小的案例 ...

随机推荐

  1. CentOS 6.8下安装MySQL 5.6.33

    此处操作,包含MySQL的客户端及服务端. MySQL下载地址: http://dev.mysql.com/downloads/mysql/5.6.html MySQL--.linux_glibc2. ...

  2. phoenix与spark整合

    目的是将phoenix做存储,spark做计算层.这样就结合了phoenix查询速度快和spark计算速度快的优点.在这里将Phoenix的表作为spark的RDD或者DataFrames来操作,并且 ...

  3. windows下用一台机器配置分布式redis(主从服务器)

    目录1.Replication的工作原理2.如何配置Redis主从复制 1.Replication的工作原理在Slave启动并连接到Master之后,它将主动发送一条SYNC命令.此后Master将启 ...

  4. PHP中float变量转换为int时,结果有误的问题!

    先上例子: <?php $money = 100; $rate = 1.15; $result = $money * $rate; var_dump( intval( $result ) ); ...

  5. C# using 三种使用方式

    http://www.cnblogs.com/fashui/archive/2011/09/29/2195061.html 1.using指令. using 命名空间名字.例如: using Syst ...

  6. 安装 python psutil 包

    yum install python-pip 下载 psutil https://pypi.python.org/pypi?:action=display&name=psutil#downlo ...

  7. EXT 省市三级联动及默认选择

    var provinceStore = Ext.create('Ext.data.Store', { fields: ['id', 'name'], proxy: { type: 'ajax', ur ...

  8. Mantis 1.2.19 on Windows Server 2012 r2 datacenter 安装及配置随笔

    一.前言 新的小团队需要搭建一个缺陷管理的工具,之前用过bugfree,感觉比较适合,但是 禅道不太适合,放弃之,于是又百度推荐的: .JTrac13.BugNet14.BugOnline15.eTr ...

  9. tinyxml学习2

    在TinyXML中,根据XML的各种元素来定义了一些类: TiXmlBase:整个TinyXML模型的基类. TiXmlAttribute:对应于XML中的元素的属性. TiXmlNode:对应于DO ...

  10. WM_CLOSE WM_DESTROY WM_QUIT的区别

    WM_CLOSE:关闭应用程序窗口 WM_DESTROY:关闭应用程序 WM_QUIT:关闭消息循环