Android AsyncTask源码简要分析

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

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

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[])
 */
@MainThread
public 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虽然提供了一种简单便捷的异步机制,但是我们还是很有必要特别关注到他的缺点,避免出现因为使用错误而导致的严重系统性能问题。

猜你喜欢

转载自blog.csdn.net/zxccxzzxz/article/details/54565165