Android应用AsyncTask处理机制详解及源码分析,移动互联网开发

Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点。前面我们分析了Handler异步机制原理(不了解的可以阅读我的《Android异步消息处理机制详解及源码分析》文章),这里继续分析Android的另一个异步机制AsyncTask的原理。

当使用线程和Handler组合实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比较大。为了提高性能我们使用AsyncTask实现异步处理(其实也是线程和handler组合实现),因为其内部使用了java提供的线程池技术,有效的降低了线程创建数量及限定了同时运行的线程数,还有一些针对性的对池的优化操作。所以说AsyncTask是Android为我们提供的方便编写异步任务的工具类。

这里写图片描述

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】

2 实例演示


先看下使用AsyncTask模拟下载的效果图:

这里写图片描述

看下代码,如下:

public class MainActivity extends Activity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

new TestAsyncTask(this).execute();

}

static final class TestAsyncTask extends AsyncTask<Void, Integer, Boolean> {

//如上三个泛型参数从左到右含义依次为:

//1. 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。

//2. 后台任务执行时,如果需要在界面上显示当前的进度,则使用这个。

//3. 当任务执行完毕后,如果需要对结果进行返回,则使用这个。

private Context mContext = null;

private ProgressDialog mDialog = null;

private int mCount = 0;

public TestAsyncTask(Context context) {

mContext = context;

}

//在后台任务开始执行之间调用,用于进行一些界面上的初始化操作

protected void onPreExecute() {

super.onPreExecute();

mDialog = new ProgressDialog(mContext);

mDialog.setMax(100);

mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);

mDialog.show();

}

//这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务

protected Boolean doInBackground(Void… params) {

while (mCount < 100) {

publishProgress(mCount);

mCount += 20;

try {

Thread.sleep(1000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

return true;

}

//当在后台任务中调用了publishProgress(Progress…)方法

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

后,这个方法就很快会被调用

protected void onProgressUpdate(Integer… values) {

super.onProgressUpdate(values);

mDialog.setProgress(values[0]);

}

//当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用

protected void onPostExecute(Boolean aBoolean) {

super.onPostExecute(aBoolean);

if (aBoolean && mDialog != null && mDialog.isShowing()) {

mDialog.dismiss();

}

}

}

}

可以看见Android帮我们封装好的AsyncTask还是很方便使用的,咱们不做过多说明。接下来直接分析源码。

【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】

3 Android5.1.1(API 22)AsyncTask源码分析


通过源码可以发现AsyncTask是一个抽象类,所以我们在在上面使用时需要实现它。

那怎么下手分析呢?很简单,我们就依据上面示例的流程来分析源码,具体如下。

3-1 AsyncTask实例化源码分析

/**

  • Creates a new asynchronous task. This constructor must be invoked on the UI thread.

*/

public AsyncTask() {

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(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);

}

}

};

}

看见注释没有,AsyncTask的实例化只能在UI线程中。然后整个构造函数就只初始化了两个AsyncTask类的成员变量(mWorker和mFuture)。mWorker为匿名内部类的实例对象WorkerRunnable(实现了Callable接口),mFuture为匿名内部类的实例对象FutureTask,传入了mWorker作为形参(重写了FutureTask类的done方法)。

3-2 AsyncTask的execute方法源码分析

正如上面实例一样,得到AsyncTask实例化对象之后就执行了execute方法,所以看下execute方法的源码,如下:

public final AsyncTask<Params, Progress, Result> execute(Params… params) {

return executeOnExecutor(sDefaultExecutor, params);

}

可以看见,execute调运了executeOnExecutor方法,executeOnExecutor方法除过传入了params形参以外,还传入了一个static的SerialExecutor对象(SerialExecutor实现了Executor接口)。继续看下executeOnExecutor源码,如下:

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;

}

首先判断AsyncTask异步任务的状态,当处于RUNNING和FINISHED时就报IllegalStateException非法状态异常。由此可以看见一个AsyncTask的execute方法只能被调运一次。接着看见17行onPreExecute();没有?看下这个方法源码,如下:

/**

  • Runs on the UI thread before {@link #doInBackground}.

  • @see #onPostExecute

  • @see #doInBackground

*/

protected void onPreExecute() {

}

空方法,而且通过注释也能看见,这不就是我们AsyncTask中第一个执行的方法吗?是的。

回过头继续往下看,看见20行exec.execute(mFuture);代码没?exec就是形参出入的上面定义的static SerialExecutor对象(SerialExecutor实现了Executor接口),所以execute就是SerialExecutor静态内部类的方法喽,在执行execute方法时还传入了AsyncTask构造函数中实例化的第二个成员变量mFuture。我们来看下SerialExecutor静态内部类的代码,如下:

private static class SerialExecutor implements Executor {

final ArrayDeque mTasks = new ArrayDeque();

Runnable mActive;

public synchronized void execute(final Runnable r) {

mTasks.offer(new Runnable() {

public void run() {

try {

r.run();

} finally {

scheduleNext();

}

}

});

if (mActive == null) {

scheduleNext();

}

}

protected synchronized void scheduleNext() {

if ((mActive = mTasks.poll()) != null) {

THREAD_POOL_EXECUTOR.execute(mActive);

}

}

}

在源码中可以看见,SerialExecutor在AsyncTask中是以常量的形式被使用的,所以在整个应用程序中的所有AsyncTask实例都会共用同一个SerialExecutor对象。

接着可以看见,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,如果我们一次性启动了很多个任务,首先在第一次运行execute()方法的时候会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的最后,然后判断mActive对象是不是等于null,第一次运行是null,然后调用scheduleNext()方法,在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如果再有新的任务被执行时就等待上一个任务执行完毕后才会得到执行,所以说同一时刻只会有一个线程正在执行,其余的均处于等待状态,这就是SerialExecutor类的核心作用。

我们再来看看上面用到的THREAD_POOL_EXECUTOR与execute,如下:

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

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 sPoolWorkQueue =

new LinkedBlockingQueue(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);

}

看见没有,实质就是在一个线程池中执行,这个THREAD_POOL_EXECUTOR线程池是一个常量,也就是说整个App中不论有多少AsyncTask都只有这一个线程池。也就是说上面SerialExecutor类中execute()方法的所有逻辑就是在子线程中执行,注意SerialExecutor的execute方法有一个Runnable参数,这个参数就是mFuture对象,所以我们看下FutureTask类的run()方法,如下源码:

public void run() {

if (state != NEW ||

!UNSAFE.compareAndSwapObject(this, runnerOffset,

null, Thread.currentThread()))

return;

try {

Callable c = callable;

if (c != null && state == NEW) {

V result;

boolean ran;

try {

result = c.call();

ran = true;

} catch (Throwable ex) {

result = null;

ran = false;

setException(ex);

}

if (ran)

set(result);

}

} finally {

// runner must be non-null until state is settled to

// prevent concurrent calls to run()

runner = null;

// state must be re-read after nulling runner to prevent

// leaked interrupts

int s = state;

if (s >= INTERRUPTING)

handlePossibleCancellationInterrupt(s);

}

}

看见没有?第7行的c = callable;其实就是AsyncTask构造函数中实例化FutureTask对象时传入的参数mWorker。12行看见result = c.call();没有?其实就是调运WorkerRunnable类的call方法,所以我们回到AsyncTask构造函数的WorkerRunnable匿名内部内中可以看见如下:

mWorker = new WorkerRunnable<Params, Result>() {

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值