Android AsyncTask 异步任务之源码解析

记得14那年导师的第一个项目让我有机会了解 Android,那时可以说是需要什么功能就去找相应的资料。针对网络请求这一模块,在 Android 4.0 之后的版本如果在主线程执行网络请求,运行时是会报错的(我当时就遭过)。因为主线程主要是负责更新界面并与用户进行交互,不能执行耗时的操作。针对这个问题,我通过百度找到的解决方案大体就是创建一个线程 Thread,在线程的 run() 方法中去执行网络请求,最后再通过 Handler 发送消息通知 UI 线程来刷新界面。

同年的暑假,我有机会去一家公司实习(公司氛围和同事都很nice)。在实习过程中慢慢的接触了今天的主角 – AsyncTask。那时可没想太多,拿过来就用啊。可能自己的无知,像 AsyncHttpClient 这么鼎鼎有名的网络请求开源库在那时我居然不知道。现在回想起来,无知者,罪过、罪过。

一、初识 AsyncTask

不过今天不是来讨论 AsyncHttpClinet 的,毕竟今天的主角可是 AsyncTask。先看看我们一般使用 AsyncTask 的操作流程:代码块1

public class MyAsyncTask extends AsyncTask<Params, Progress, Result> {

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    protected Result doInBackground(Params... params) {
        // do something
        ...
        return null;
    }

    @Override
    protected void onPostExecute(Result result) {
        super.onPostExecute(s);
    }
}

new MyAsyncTask().execute(...);

首先我们先是定义一个类 MyAsyncTask 并让其继承 AsyncTask。由于 doInBackground() 方法是抽象的,所以必须重写。当然,我们也会根据需求去重写其他方法,如 onPreExecute()、onPostExecute()、onPreogressUpdate()等。

  1. onPreExecute(): 在这个方法里一般是进行一些初始化的工作;
  2. doInbackground(): 我们会在这个方法里执行如网络请求等操作,同时根据需要将结果返回;
  3. onProgressUpdate(): 显示任务执行的进度。不过我们要在 doInBackground() 方法手动调用 publishProgress() 方法后这个方法才能得到响应。
  4. onPostExecute(): 这个方法接收任务执行后返回的结果。

二、AsyncTask 的核心源头

好了,万事俱备,只欠执行啦 new MyAsyncTask().execute()。来看看 execute() 方法具体是做了那些事:代码块2

#AsyncTask.java

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

@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
            Params... params) {
    // mStatus 属性看下面定义        
    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;
}

private volatile Status mStatus = Status.PENDING;
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,
}

可以看到,execute() 方法会跳转到 executeOnExecutor()。补充一下:Status 是一个枚举类,而 mStatus 的默认初始值为 Status.PENDING,表示当前任务是处于待办状态。首先先是对 mStatus 进行判断,如果当前任务不是处于 Status.PENDING,则会报异常。这也是为什么我们不能执行多次 execute() 的原因。

顺利的情况下,mStatus 会被置成运行状态 Status.RUNNING;接着调用 onPreExecute(),我们可以在该方法里进行一些初始化操作。然后就是将我们传递进来的参数存放在 mWorker.mParams 里,看看 mWoeker 是什么玩意吧:代码块3

#AsyncTask.java

private final WorkerRunnable mWorker;

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
    Params[] mParams;
}

public AsyncTask() {
    mWorker = new WorkerRunnable() {
        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);
        }
    };

    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 occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

原来在 AsyncTask 的构造方法中创建了两个对象:mWorker 和 mFuture。源码写得很清楚,mWorker 是一个 WorkerRunnable

#AsyncTask.java

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

public static final Executor SERIAL_EXECUTOR = new 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);
        }
    }
}

public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

我们先理一理吧。在执行 execute() 方法后,最終会调用 sDefaultExecutor.execute()。在这方法里,首先会将我们传递进来的 mFuture 封装到一个 Runnable 对象中,同时再将这个 Runnable 对象将添加到任务队列 mTasks 中。然后判断 mActive 为空时则执行 scheduleNext()。这个方法做的事就是从队列中取出消息,然后交给 THREAD_POOL_EXECUTOR 去执行。而 THREAD_POOL_EXECUTOR 是一个线程池执行器 ThreadPoolExecutor。让我们一起跳转到其 execute() 方法里:

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();

    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

这里最主要的还是addWorker() 操作:

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

上面方法做的事情就是将 firstTask 作为参数拿来创建一个 Worker 对象 w,然后通过 w 获得线程实例 thread,最后就是调用这个线程的 start() 方法。这里我们要明确一下:此时 Worker 对象中的 Thread 属性所持有的 Runnable 对象其实是 代码4 中新创建的 Runnable, 而我们真正的任务是在其 run() 方法中,看下面代码:

#AsyncTask.java

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

运行 Worker.thread.start() 最終会运行上面的 run() 方法。在该 run() 方法中又会调用传递进来的参数 Runnnable 对象的 run()方法,而这个传递进来的参数其实就是 mFuture,可回头看 代码块2。好吧,请允许我先喘口气… 在前面我们已经知道 mFuture 是在 AsyncTask 的构造方法中进行初始化的。现在让我们直接跳转到 mFuture 所属类型 – FutureTask 类的 run() 方法:

#FutureTask.java

public void run() {
    if (state != NEW ||
        !U.compareAndSwapObject(this, RUNNER, 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);
    }
}

该方法会最終会执行 callable 对象的 call 方法。那这个 callable 是什么时候初始化的呢?还记得在 AsyncTask 的构造起中创建 mFuture 时会传递 mWorker 参数:

public AsyncTask() {
    mWorker = new WorkerRunnable() {
        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);
        }
    };

    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 occurred while executing doInBackground()",
                        e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

这样一下就明白了,执行 callable 的 call() 方法其实就是调用上面 WorkRunnable 类中的 call() 方法。在该方法中看到了熟悉的代码:Result result = doInBackground(mParams);

原来我们的 doInBackground() 方法是在这里被调用的。当后台任务执行完后,会接着调用 postResult()

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

在这里会向主线程发送一个消息,表示后台任务已经执行完成。具体的消息处理方法如下:

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

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

protected void onProgressUpdate(Progress... values) {
    }

消息类型有两种:MESSAGE_POST_RESULT 和 MESSAGE_POST_PROGRESS,分别代表任务执行完毕(正常执行完成或被中断)和任务正在处理。

总结

好了,现在是不是对整个 AsyncTask 的工作流程有个概念啦,其实归根结底还是通过 Thread+Handler,只不过这里引入线程池的概念 ThreadPoolExecutor,这使得任务调度管理和执行效率得以提升。前面我提到了网络开源框架 AsyncHttpClient,里面已经为我们做了很多准备工作了。之后我会将我做项目时基于 AsyncHttpClient 进行二次封装的库分享给大家。尽情期待哈 ^_^

你可能感兴趣的:(android)