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

}

};

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

public Result call() throws Exception {

mTaskInvoked.set(true);

Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

//noinspection unchecked

return postResult(doInBackground(mParams));

}

};

看见没有?在postResult()方法的参数里面,我们可以看见doInBackground()方法。所以这验证了我们上面例子中使用的AsyncTask,首先在主线程执行onPreExecute方法,接着在子线程执行doInBackground方法,所以这也就是为什么我们可以在doInBackground()方法中去处理耗时操作的原因了,接着等待doInBackground方法耗时操作执行完毕以后将返回值传递给了postResult()方法。所以我们来看下postResult这个方法的源码,如下:

private Result postResult(Result result) {

@SuppressWarnings(“unchecked”)

Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,

new AsyncTaskResult(this, result));

message.sendToTarget();

return result;

}

先看下这个getHandler拿到的是哪个Handler吧,如下:

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;

}

}

}

看见没有,拿到的是MainLooper,也就是说在在UI线程中的Handler(不清楚的请阅读《Android异步消息处理机制详解及源码分析》文章)。所以上面的方法其实就是将子线程的数据发送到了UI来处理,也就是通过MESSAGE_POST_RESULT在handleMessage来处理。所以我们继续看handleMessage中的result.mTask.finish(result.mData[0]);就会发现finish的代码如下:

private void finish(Result result) {

if (isCancelled()) {

onCancelled(result);

} else {

onPostExecute(result);

}

mStatus = Status.FINISHED;

}

看见没有?依据返回值true与false回调AsyncTask的onPostExecute或者onCancelled方法。

到此是不是会好奇onProgressUpdate方法啥时候调运的呢?继续往下看可以发现handleMessage方法中的MESSAGE_POST_PROGRESS不就是回调我们UI Thread中的onProgressUpdate方法吗?那怎么样才能让他回调呢?追踪MESSAGE_POST_PROGRESS消息你会发现如下:

protected final void publishProgress(Progress… values) {

if (!isCancelled()) {

getHandler().obtainMessage(MESSAGE_POST_PROGRESS,

new AsyncTaskResult(this, values)).sendToTarget();

}

}

额,没意思了。这不就是我们上面例子中的在子线程的doInBackground耗时操作中调运通知回调onProgressUpdate的方法么。

看见没有,AsyncTask的实质就是Handler异步消息处理机制(不清楚的请阅读《Android异步消息处理机制详解及源码分析》文章),只是对线程做了优化处理和封装而已。

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

4 为当年低版本AsyncTask的臭名正身


接触Android比较久的可能都知道,在Android 3.0之前是并没有SerialExecutor这个类的(上面有分析)。那些版本的代码是直接创建了指定大小的线程池常量来执行task的。其中MAXIMUM_POOL_SIZE = 128;,所以那时候如果我们应用中一个界面需要同时创建的AsyncTask线程大于128(批量获取数据,譬如照片浏览瀑布流一次加载)程序直接就挂了。所以当时的AsyncTask因为这个原因臭名昭著。

回过头来看看现在高版本的AsyncTask,是不是没有这个问题了吧?因为现在是顺序执行的。而且更劲爆的是现在的AsyncTask还直接提供了客户化实现Executor接口功能,使用如下方法执行AsyncTask即可使用自定义Executor,如下:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

同时我经过多年的收藏目前也算收集到了一套完整的学习资料以及高清详细的Android架构进阶学习导图及笔记分享给大家,希望对想成为架构师的朋友有一定的参考和帮助。

下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。

不论遇到什么困难,都不应该成为我们放弃的理由!

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

2.imgtp.com/2024/03/13/H4lCoPEF.jpg" />

最后

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

同时我经过多年的收藏目前也算收集到了一套完整的学习资料以及高清详细的Android架构进阶学习导图及笔记分享给大家,希望对想成为架构师的朋友有一定的参考和帮助。

下面是部分资料截图,诚意满满:特别适合有开发经验的Android程序员们学习。

[外链图片转存中…(img-RdmXN4Le-1712555692064)]

不论遇到什么困难,都不应该成为我们放弃的理由!

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言,一定会认真查询,修正不足,谢谢。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值