AsyncTask源码解析
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AsyncTask源码解析相关的知识,希望对你有一定的参考价值。
本文分为以下几部分:
1.AsyncTask的使用介绍
2.AsyncTask的实现逻辑
3.其它牵涉到的概念
1.AsyncTask的使用
class DownloadTask extends AsyncTask<Void, Integer, Boolean> { @Override protected void onPreExecute() { progressDialog.show(); } @Override protected Boolean doInBackground(Void... params) { try { while (true) { int downloadPercent = doDownload(); publishProgress(downloadPercent); if (downloadPercent >= 100) { break; } } } catch (Exception e) { return false; } return true; } @Override protected void onProgressUpdate(Integer... values) { progressDialog.setMessage("当前下载进度:" + values[0] + "%"); } @Override protected void onPostExecute(Boolean result) { progressDialog.dismiss(); if (result) { Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show(); } } }
class DownloadTask extends AsyncTask<Void, Integer, Boolean> { …… }
这里我们把AsyncTask的第一个泛型参数指定为Void(doInBackground(Params...)),表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位(onProgressUpdate(Progress...))。第三个泛型参数指定为Boolean(onPostExecute(Result)),则表示使用布尔型数据来反馈执行结果。
1. onPreExecute()
这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
2. doInBackground(Params...)---只有在这个方法中执行耗时操作
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。
3. onProgressUpdate(Progress...)
当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4. onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
执行:执行如下代码,就开启一个下载任务(实际可能只是放到队列中,没有开始执行)
new DownloadTask().execute();
需要传递参数给后台任务,那么如何定义?
这样定义:
class DownloadTask extends AsyncTask<String, Integer, Boolean>{ @Override protected Boolean doInBackground(String... params) { .... // String url = params[0]; ,这个就是客户端传入的参数 // String url2 = params[1]; 客户端传入的参数 } } //使用 DownloadTask task = new DownloadTask(); task.execute("url","url2");
2.AsyncTask的实现逻辑
1).AsyncTask是一个抽象类,子类继承抽象类,要实现抽象类的抽象方法
2).要实现的抽象方法:
抽象类AsyncTask只有一个抽象方法doInBackground,子类是必须要实现的
/** * Override this method to perform a computation on a background thread. The * specified parameters are the parameters passed to {@link #execute} * by the caller of this task. * * This method can call {@link #publishProgress} to publish updates * on the UI thread. * * @param params The parameters of the task. * * @return A result, defined by the subclass of this task. * * @see #onPreExecute() * @see #onPostExecute * @see #publishProgress */ @WorkerThread protected abstract Result doInBackground(Params... params);
3).创建子类的一个实例,然后执行execute方法,这样就开启一个AsyncTask任务。
那么,这个时候,要查看创建一个实例,然后执行execute方法,都做了什么。
实例化AsyncTask时做的事情:
/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ public AsyncTask() { 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); } }; 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); } } }; } private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
调用execute方法做的事情:
/** * Executes the task with the specified parameters. The task returns * itself (this) so that the caller can keep a reference to it. * * <p>Note: this function schedules the task on a queue for a single background * thread or pool of threads depending on the platform version. When first * introduced, AsyncTasks were executed serially on a single background thread. * Starting with {@link android.os.Build.VERSION_CODES#DONUT}, this was changed * to a pool of threads allowing multiple tasks to operate in parallel. Starting * {@link android.os.Build.VERSION_CODES#HONEYCOMB}, tasks are back to being * executed on a single thread to avoid common application errors caused * by parallel execution. If you truly want parallel execution, you can use * the {@link #executeOnExecutor} version of this method * with {@link #THREAD_POOL_EXECUTOR}; however, see commentary there for warnings * on its use. * * <p>This method must be invoked on the UI thread. * * @param params The parameters of the task. * * @return This instance of AsyncTask. * * @throws IllegalStateException If {@link #getStatus()} returns either * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. * * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) * @see #execute(Runnable) */ @MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } /** * An {@link Executor} that can be used to execute tasks in parallel. */ public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
似乎,阅读完注释,就知道它的执行原理了。
执行execute,就是创建一个任务,把这个任务加入到一个队列中,然后等待线程(可以是单一的线程,也可以是从线程池中取出来的线程)来执行。因为,所有的实例都是使用同一个THREAD_POOL_EXECUTOR(是一个静态final变量,当某个类首次加载的时候,就会初始化,之后就是供所有的实例使用)。那么,每当我们new DownloadTask().execute("url"),创建一个任务,然后将任务推给队列,这个执行的顺序是怎样的呢?
在早期的系统版本(低于android.os.Build.VEERSION_CODES#DONUT的版本),是采用串行的方式执行,某一个时刻只有一个任务在执行;从系统版本android.os.Build.VERSION_CODES#DONUT开始,修改为并行的方式执行,多个任务,分配给多个线程,并行执行;到了版本android.os.Build.VERSION_CODES#HONEYCOMB,它又修改为串行的方式。真是折腾。
并行执行的问题,看应用场景,如果某些任务是要按顺序一个一个执行的,对执行顺序有要求的,那么这些任务就不能采用并行的方式执行,而要采用串行的方式执行。因为并行执行,它不能确保所有任务的执行顺序。
现在默认是串行的方式,如果需要该任务是并行执行的,那么,使用如下方式:
DownloadTask task = new DownloadTask(); task.executeOnExecutor(THREAD_POOL_EXECUTOR,"url");
4)executeOnExecutor(Executor exec,Params... params)执行细节
private final FutureTask<Result> mFuture; /** * An {@link Executor} that can be used to execute tasks in parallel. */ public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); /** * 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 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); } } } /** * Executes the task with the specified parameters. The task returns * itself (this) so that the caller can keep a reference to it. * * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to * allow multiple tasks to run in parallel on a pool of threads managed by * AsyncTask, however you can also use your own {@link Executor} for custom * behavior. * * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from * a thread pool is generally <em>not</em> what one wants, because the order * of their operation is not defined. For example, if these tasks are used * to modify any state in common (such as writing a file due to a button click), * there are no guarantees on the order of the modifications. * Without careful work it is possible in rare cases for the newer version * of the data to be over-written by an older one, leading to obscure data * loss and stability issues. Such changes are best * executed in serial; to guarantee such work is serialized regardless of * platform version you can use this function with {@link #SERIAL_EXECUTOR}. * * <p>This method must be invoked on the UI thread. * * @param exec The executor to use. {@link #THREAD_POOL_EXECUTOR} is available as a * convenient process-wide thread pool for tasks that are loosely coupled. * @param params The parameters of the task. * * @return This instance of AsyncTask. * * @throws IllegalStateException If {@link #getStatus()} returns either * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. * * @see #execute(Object[]) */ @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; }
a.SerialExecutor的实现逻辑:
1).mTasks.offer(Runnable),创建一个新的Runnable,添加到队列尾部。这个新的Runnable就是交给THREAD_POOL_EXECUTOR执行的任务。新的Runnable包含了从客户端提交的任务。
新的Runnable的逻辑是:先执行客户端提交的任务(也就是mFuture);然后,执行scheduleNext方法,scheduleNext做的事情,从队列的头部取出创建的Runnable,然后交给THREAD_POOL_EXECUTOR执行。
通过这种方式,就可以保证所有的任务是按照串行的方式来执行的。新添加的mFuture,使用新创建的一个Runnable,然后,将这个Runnable添加到队列尾部;然后,每次执行的时候,是从队列的的头部取添加的Runnable。
2).触发执行的入口是:
if (mActive == null) {
scheduleNext();
}
3).scheduleNext方法,是同步方法,确保每次只有一个线程可以执行该方法。THREAD_POOL_EXECUTOR有多个线程。
protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); }
通过SerialExecutor,就可以实现串行的执行。
实现逻辑总结:
1.执行从在executeOnExecutor开始,在该方法中调用 onPreExecute(),这是在UI线程执行的。
2.当THREAD_POOL_EXECUTOR有空闲线程时,就会最终执行mFuture任务,而mFuture包含mWorker,它会执行:Result result = doInBackground(mParams);----mFuture是在第三方线程中运行的
3.将执行的结果发送给UI线程:
在mWorker中会调用postResult方法;postResult方法,通过UI线程的handler发送消息。这里的关键是InternalHandler的创建,它使用的是Ui线程的Looper,这样消息才会给Ui线程的消息循环处理。
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } } 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; } } }
4.执行过程中的进度更新:
因为该方法是在第三方线程中运行的,所以,同样是使用Ui线程的Handler来发送消息
/** * This method can be invoked from {@link #doInBackground} to * publish updates on the UI thread while the background computation is * still running. Each call to this method will trigger the execution of * {@link #onProgressUpdate} on the UI thread. * * {@link #onProgressUpdate} will not be called if the task has been * canceled. * * @param values The progress values to update the UI with. * * @see #onProgressUpdate * @see #doInBackground */ @WorkerThread protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
3.其它牵涉到的概念介绍
1.FutureTask
2.Callable
引用:
部分示例代码引用自网络
以上是关于AsyncTask源码解析的主要内容,如果未能解决你的问题,请参考以下文章