Android AsyncTask源码简要分析

来源:互联网 发布:福利软件 编辑:程序博客网 时间:2024/05/19 09:13

AsyncTask

初学 Android 的线程使用时接触最多的就是 AsyncTask 了,直到现在都还没有仔细查看过这个类的源码和实现,有愧于心啊。不说多的,本篇主要根据 AsyncTask 的类结构来进行简要说明,大致的介绍官方都写在文档或者注释中了,我们需要仔细研读。

AysncTask 只适用于最多几秒的操作场景,如果有更高的需求官方强烈推荐使用 Executor / ThreadPoolExecutor / FutureTask 等。AsyncTask分为几个部分:

postResult

当 doInBackground() 方法执行完毕后会将异步执行得到的结果回调给 postResult(),postResult() 将数据抛给InternalHandler。

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

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

我们看到 InternalHandler 的两种 case,前者调用了 finish() 传递数据到 onPostExecute() 方法,后者调用 onProgressUpdate() 方法更新进度条。AsyncTask 能够进行 UI 操作实际就是通过 InternalHandler 进行的

private void finish(Result result) {    if (isCancelled()) {        onCancelled(result);    } else {        onPostExecute(result);    }    mStatus = Status.FINISHED;}

在AsyncTask中内部获取 Handler 是由单例模式生成的实例:

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

AsyncTaskResult

充当 Entity 的作用。调用 onPostExecute 的时候是通过 handler 先将数据放在 AsyncTaskResult 对象中再传递到InternalHandler 中进行UI操作

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

ThreadPoolExecutor

AsyncTask 实际就是通过线程池实现的异步队列,可以不停的往队列中添加任务,边取边放以达到多线程的操作。

/** * An {@link Executor} that can be used to execute tasks in parallel. */public static final Executor THREAD_POOL_EXECUTOR;static {    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(            CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,            sPoolWorkQueue, sThreadFactory);    threadPoolExecutor.allowCoreThreadTimeOut(true);    THREAD_POOL_EXECUTOR = threadPoolExecutor;}

Status

枚举类,表示当前Task的状态

 /**     * Indicates the current status of the task. Each status will be set only once     * during the lifetime of a task.     */    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,    }

Binder

AsyncTask 构造函数开始就使用了Binder的方法:

/**     * Flush any Binder commands pending in the current thread to the kernel     * driver.  This can be     * useful to call before performing an operation that may block for a long     * time, to ensure that any pending object references have been released     * in order to prevent the process from holding on to objects longer than     * it needs to.     */    public static final native void flushPendingCommands();

WorkerRunnable

在构造函数初始化时有两个部分,一个是mWorker,另一个是mFuture。前者实现了Callable接口,目的是为的是接收外部传来的参数,通过实现Callable,以 call() 方法将用户参数传入 doInBackground() 得到 Result。

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

Future

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

executeOnExecutor

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

execute

想要启动AsyncTask,就必须执行此方法,方法中使用了sDefaultExecutor

/** * Convenience version of {@link #execute(Object...)} for use with * a simple Runnable object. See {@link #execute(Object[])} for more * information on the order of execution. * * @see #execute(Object[]) * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) */@MainThreadpublic static void execute(Runnable runnable) {    sDefaultExecutor.execute(runnable);}

SerialExecutor

关键部分,AsyncTask 中的队列就是这个类的 ArrayDeque 实现的,最后 execute 执行的是这个类中的方法。

/** * 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 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);            }        }    }

AsyncTask 的使用注意事项

在官方的 性能优化典范(五) (中文戳这儿 —> ) Android性能优化典范 - 第5季中已经写得很明确了,以下引用自胡凯大神的译文

4)Good AsyncTask Hunting

AsyncTask是一个让人既爱又恨的组件,它提供了一种简便的异步处理机制,但是它又同时引入了一些令人厌恶的麻烦。一旦对AsyncTask使用不当,很可能对程序的性能带来负面影响,同时还可能导致内存泄露。

举个例子,常遇到的一个典型的使用场景:用户切换到某个界面,触发了界面上的图片的加载操作,因为图片的加载相对来说耗时比较长,我们需要在子线程中处理图片的加载,当图片在子线程中处理完成之后,再把处理好的图片返回给主线程,交给UI更新到画面上。

img

AsyncTask的出现就是为了快速的实现上面的使用场景,AsyncTask把在主线程里面的准备工作放到onPreExecute()方法里面进行执行,doInBackground()方法执行在工作线程中,用来处理那些繁重的任务,一旦任务执行完毕,就会调用onPostExecute()方法返回到主线程。

img

使用AsyncTask需要注意的问题有哪些呢?请关注以下几点:

  • 首先,默认情况下,所有的AsyncTask任务都是被线性调度执行的,他们处在同一个任务队列当中,按顺序逐个执行。假设你按照顺序启动20个AsyncTask,一旦其中的某个AsyncTask执行时间过长,队列中的其他剩余AsyncTask都处于阻塞状态,必须等到该任务执行完毕之后才能够有机会执行下一个任务。情况如下图所示:

img

为了解决上面提到的线性队列等待的问题,我们可以使用AsyncTask.executeOnExecutor()强制指定AsyncTask使用线程池并发调度任务。

img

  • 其次,如何才能够真正的取消一个AsyncTask的执行呢?我们知道AsyncTaks有提供cancel()的方法,但是这个方法实际上做了什么事情呢?线程本身并不具备中止正在执行的代码的能力,为了能够让一个线程更早的被销毁,我们需要在doInBackground()的代码中不断的添加程序是否被中止的判断逻辑,如下图所示:

img

一旦任务被成功中止,AsyncTask就不会继续调用onPostExecute(),而是通过调用onCancelled()的回调方法反馈任务执行取消的结果。我们可以根据任务回调到哪个方法(是onPostExecute还是onCancelled)来决定是对UI进行正常的更新还是把对应的任务所占用的内存进行销毁等。

  • 最后,使用AsyncTask很容易导致内存泄漏,一旦把AsyncTask写成Activity的内部类的形式就很容易因为AsyncTask生命周期的不确定而导致Activity发生泄漏。

img

综上所述,AsyncTask虽然提供了一种简单便捷的异步机制,但是我们还是很有必要特别关注到他的缺点,避免出现因为使用错误而导致的严重系统性能问题。

0 0