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()等。
- onPreExecute(): 在这个方法里一般是进行一些初始化的工作;
- doInbackground(): 我们会在这个方法里执行如网络请求等操作,同时根据需要将结果返回;
- onProgressUpdate(): 显示任务执行的进度。不过我们要在 doInBackground() 方法手动调用 publishProgress() 方法后这个方法才能得到响应。
- onPostExecute(): 这个方法接收任务执行后返回的结果。
二、AsyncTask 的核心源头
好了,万事俱备,只欠执行啦 new MyAsyncTask().execute()
。来看看 execute() 方法具体是做了那些事:代码块2
#AsyncTask.java@MainThreadpublic final AsyncTask execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params);}@MainThreadpublic 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.javaprivate 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.javaprivate 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.javapublic 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.javapublic 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应用资源的使用方法(数组、颜色、尺寸、字符串、布尔、整
- 源码阅读分析 - View的Touch事件分发
- 随Android生命周期解绑Rxjava订阅的简单流式方法
- Android(安卓)Touch点击事件源码分析
- 【原创】Android(安卓)系统稳定性 - ANR(三)
- Android工程引用另外一个工程的正确/错误方法
- Android混合开发(一)——移动端与前端交互之JSBridge引入
- 简述Android(安卓)解决65536/64K方法数限制方案
- Android(安卓)—— 自定义View的实现方法