AsyncTask源码解析

AsyncTask类是Android设计的一个执行异步方法的类,我们可以利用这个类执行一些耗时的任务,然后根据结果更新UI,在执行任务时,也可以更新UI,如设置进度条的进度等等。今天,我们从源码分析这个类,学习执行异步方法的思想,这对我写异步框架应该会起到帮助。Show me the code !!

从属性开始,我分为2部分:(1)线程池的初始化。(2)普通属性。

(1)线程池的初始化
直接看代码:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE_SECONDS = 30;
// 线程工厂,用来为线程池创建工作线程
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());
    }
};
// 任务队列,当线程池的工作线程大于核心线程CORE_POOL_SIZE,新的任务便会加入到这个任务队列中,这里定义队列大小为128
private static final BlockingQueue<Runnable> sPoolWorkQueue =
        new LinkedBlockingQueue<Runnable>(128);
// 真正执行任务的线程池
public static final Executor THREAD_POOL_EXECUTOR;
// 静态块
static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,       // 核心线程数
            MAXIMUM_POOL_SIZE,    // 最大线程数
            KEEP_ALIVE_SECONDS,   // 非核心线程闲置的时间
            TimeUnit.SECONDS,     // KEEP_ALIVE_SECONDS的单位
            sPoolWorkQueue,       // 任务队列
            sThreadFactory);      // 线程工厂
    // 设置核心线程闲置时,超过KEEP_ALIVE_SECONDS也被销毁,这里是1秒
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

在静态块中,一开始便初始化了一个线程池。更多关于线程池,请参考我上一篇博文Android线程池浅析

(2)普通属性

// 这个是加入任务的线程池,接下来会分析
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
// Handler处理消息的标志
private static final int MESSAGE_POST_RESULT = 0x1;
private static final int MESSAGE_POST_PROGRESS = 0x2;
// 默认执行任务的线程池,定义为SerialExecutor,待会分析
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();

普通属性我们简单的分析了一下,上面提到SerialExecutor线程池,我们来看看代码:

private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
    public synchronized void execute(final Runnable r) {
        // 将Runnable任务加入到mTasks队列中
        mTasks.offer(new Runnable() {
            public void run() {
                try {
                    r.run();
                } finally {
                    scheduleNext();
                }
            }
        });
        // 如果没有任务在执行,就开始执行任务
        if (mActive == null) {
            scheduleNext();
        }
    }
    protected synchronized void scheduleNext() {
        // 从mTasks队列取出任务,并加入到THREAD_POOL_EXECUTOR线程池执行任务
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }
}

从代码可以看出,SerialExecutor是临时保存任务的,真正执行任务的是THREAD_POOL_EXECUTOR线程池。从代码还看出,AsyncTask是串行执行的,这点很重要。还涉及到队列的小知识,如果mTasks没有元素Runnable的话,调用mTasks.poll()会返回null,不会阻塞。
属性中还定义了状态,有以下3种:

 public enum Status {
    /**
     * Indicates that the task has not been executed yet.
     */
    PENDING,
    /**
     * Indicates that the task is running.
     */
    RUNNING,
    /**
     * Indicates that {@link AsyncTask#onPostExecute} has finished.
     */
    FINISHED,
}

好了,属性部分到这里已经看完了,我们看看构造函数吧:

public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            ...
        }
    };
    mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            ...
        }
    };
}

构造函数主要初始化mFuture,里面的实现代码再分析。为什么要使用FutureTask呢?那是因为AsyncTask任务需要可取消的功能,而FutureTask正好满足需求。关于FutureTask我也写了一篇博文,请参考FutureTask源码浅析
我们要执行AsyncTask时,要调用它的execute,代码如下:

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

@MainThread表示要在主线程执行,至于为什么,待会我一一道来。我们继续跟踪executeOnExecutor方法:

@MainThread
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
        Params... params) {
    // 如果当前状态不是Status.PENDING,直接报错。
    // 这就表明了,一个AsyncTask只能执行一次。
    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
    mWorker.mParams = params;
    // 开始执行mFuture任务
    exec.execute(mFuture);
    return this;
}

我们先来看看mWorker。它是一个WorkerRunnable,实现Callable接口,如果你知道FutureTask,应该知道为什么要实现Callable。

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    // 定义了参数,AsyncTask就是用过这个属性,把参数传进来使用的
    Params[] mParams;
}

调用exec.execute(mFuture)便开始执行任务,上面已经分析过了,其实是先加入一个队列,然后交给THREAD_POOL_EXECUTOR线程池开启线程执行。
我们看看mFuture执行任务的代码,那么得回到初始化mFuture的代码了,也就是构造函数:

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        // 设置这个任务已经在执行了
        mTaskInvoked.set(true);
        // 设置线程优先级,处于THREAD_PRIORITY_BACKGROUND
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        // noinspection unchecked
        // 这里开始执行我们子类重写的doInBackground方法
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        // 调用postResult方法,里面主要是调用
        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);
        }
    }
};

如果你对FutureTask熟悉的话,应该知道,FutureTask执行任务会先调用Callable的call方法。上面的代码中,mWorker会先执行call方法,然后mFuture的done方法才会调用。mWorker的call方法中,先调用了我们子类重写的doInBackground方法,然后调用了postResult方法。我们看看postResult方法的代码:

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

从代码可以知道,postResult就是把任务执行的结果通过Handler(消息机制)传到主线程。如果对消息机制不了的,可以看我写的3篇关于消息机制的博文Android消息机制(1)- 简介。getHandler得到的是主线程的Handler,代码如下:

private static Handler getHandler() {
    synchronized (AsyncTask.class) {
        if (sHandler == null) {
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}

我们再来看看自定义的InternalHandler代码:

private static class InternalHandler extends Handler {
    public InternalHandler() {
        // 主线程Looper
        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;
        }
    }
}

看InternalHandler重写的handleMessage方法,主要处理2个消息事件:MESSAGE_POST_RESULT和MESSAGE_POST_PROGRESS。postResult方法传过来的是MESSAGE_POST_RESULT事件,这里涉及到一个类,AsyncTaskResult,代码:

private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;
    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

其实就是一个保存数据和AsyncTask的包装类。
我们回到InternalHandler处理MESSAGE_POST_RESULT事件,调用了result.mTask.finish,从AsyncTaskResult看出,其实也就是执行AsyncTask的finish方法,代码如下:

private void finish(Result result) {
    if (isCancelled()) {
        // 如果任务取消了,调用onCancelled方法
        onCancelled(result);
    } else {
        // 如果任务没有取消,调用onPostExecute方法
        onPostExecute(result);
    }
    // 更新状态
    mStatus = Status.FINISHED;
}

finish方法代码很简单,不用多说。InternalHandler还可以处理一个事件,MESSAGE_POST_PROGRESS,作用是在执行doInBackground方法时,还能通过调用publishProgress方法更新UI。
我们先看看publishProgress方法:

@WorkerThread
protected final void publishProgress(Progress... values) {
    if (!isCancelled()) {
        // 如果任务没有取消,分发消息给InternalHandler处理
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

主要是分发消息给InternalHandler处理,InternalHandler调用result.mTask.onProgressUpdate(result.mData)方法处理事件,也就是交给AsyncTask的onProgressUpdate方法处理。而onProgressUpdate是一个挂钩方法,交给子类去实现。
接下来给出子类可以重写和必须重写的方法,主要是看一下:

// 子类必须重写,执行异步的方法
@WorkerThread
protected abstract Result doInBackground(Params... params);
// 其他方法子类可以选择重写,挂钩方法
@MainThread
protected void onPreExecute() {
}
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onPostExecute(Result result) {
}
@SuppressWarnings({"UnusedDeclaration"})
@MainThread
protected void onProgressUpdate(Progress... values) {
}
@SuppressWarnings({"UnusedParameters"})
@MainThread
protected void onCancelled(Result result) {
    onCancelled();
}    
@MainThread
protected void onCancelled() {
}

到这里,相信你对AsyncTask的机制已经很熟悉了。还有一个,AsyncTask是可取消的,我们看看取消的代码:

public final boolean cancel(boolean mayInterruptIfRunning) {
    // 设置取消标志
    mCancelled.set(true);
    // 调用FutureTask的取消方法
    return mFuture.cancel(mayInterruptIfRunning);
}
// 判断是否取消
public final boolean isCancelled() {
    return mCancelled.get();
}

FutureTask调用cancel方法传入了一个参数,通过解读FutureTask源码可知,如果参数mayInterruptIfRunning为true的话,会立刻中断线程,并设置Interrupt标志。如果为false,则会继续等待线程执行完,设置Cancel标志。
我还在想AsyncTask的cancel方法为什么没有调用onCancelled方法呢,原来我之前对中断线程有误解,正好这里更正了我对线程中断的理解。我原以为调用Thread的interrupt()方法,run方法就不会继续往下执行,直接跳出run方法,这种想法完全是错误的。其实调用Thread的interrupt()方法,如果Thread处于阻塞状态,如处于sleep或者wait,此时,Thread就会收到中断的信号,提前终止阻塞状态,同时抛出InterruptedException异常,然后继续往下执行。如果Thread并没有阻塞,调用Thread的interrupt()方法并不会起效果,run方法还是会继续执行下去。为了证明这点,我特意写了个demo,代码:

public class MainActivity extends Activity {
    // 阻塞队列
    private LinkedBlockingQueue<Integer> queue;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        queue = new LinkedBlockingQueue<>();
        final Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                int result;
                try {
                    // 从队列取出元素
                    result = queue.take();
                } catch (InterruptedException e) {
                    // 处理Interrupted异常
                    e.printStackTrace();
                    result = -1;
                }
                Log.e(getClass().getName(), "结果为" + result);
                sayHello();
            }
        });
        thread.start();
        // 2秒后调用thread.interrupt方法
        new Handler().postDelayed(new Runnable() {
            @Override
            public void run() {
                thread.interrupt();
            }
        }, 2000);
    }
    private void sayHello() {
        Log.e(getClass().getName(), "hello, i executed ----------- ");
    }
}

打印的结果如下:
打印结果
从打印结果可以知道,sayHello方法还是会执行的,证明我刚才对Thread.interrupt()方法的理解是正确的。

我们再来看调用AsyncTask的cancel(true)方法(因为false不会中断任务线程),我们结合mFuture初始化代码看:

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        // 设置这个任务已经在执行了
        mTaskInvoked.set(true);
        // 设置线程优先级,处于THREAD_PRIORITY_BACKGROUND
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        // noinspection unchecked
        // 这里开始执行我们子类重写的doInBackground方法
        Result result = doInBackground(mParams);
        Binder.flushPendingCommands();
        // 调用postResult方法,里面主要是调用
        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);
        }
    }
};

因为doInBackground是异步方法,一般在这里阻塞,调用FutureTask的cancel方法后,doInBackground提前结束阻塞,继续往下执行,那么postResult方法也会被执行,接着调用finish方法:

private void finish(Result result) {
    if (isCancelled()) {
        // 如果任务取消了,调用onCancelled方法
        onCancelled(result);
    } else {
        // 如果任务没有取消,调用onPostExecute方法
        onPostExecute(result);
    }
    // 更新状态
    mStatus = Status.FINISHED;
}

此时isCancelled为true,所以onCancelled会被执行。这里有学到了关于线程中断的一点小知识,太激动了!!!
到此,整个AsyncTask的原理分析的差不多了,其实我们也能利用这种思想做出我们自己异步框架。我们不是重新做轮子,我们的目标是做更好的轮子。浏览了一下源码,还有个地方你应该知道:

public static void setDefaultExecutor(Executor exec) {
    sDefaultExecutor = exec;
}

其实我们可以自定义AsyncTask执行任务的线程池和任务排序,默认是顺序执行,如果你想要任务按照你想要的顺序排序,可以自定义Executor,代码回忆一下:

...
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    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);
        }
    }
}
...

如果你并不想要重新定义顺序,只想自定义线程池,执行方法时,调用executeOnExecutor,以下是execute和executeOnExecutor的区别:

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

AsyncTask源码到这里分析已经结束了,发现自己的好好补补线程和队列的知识。

AsyncTask源码下载地址

AsyncTask源码

参考资料

JAVA线程的interrupt

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值