AsyncTask机制详解
AsyncTask是Android提供的一个轻量级异步任务机制,使用AsyncTask可以方便的执行异步任务,并将结果更新到main thread。AsyncTask中是通过Handler机制来让work thread和main thread通信的。如果你对Handler还不了解的话,可以通过我的这篇博客来了解Android的Handler机制。Android 异步消息处理机制
在这篇文章中我们将了解AsyncTask的基本用法以及从源码的角度来分析AsyncTask机制,首先我们来了解下开发过程中AsyncTask的使用
AsyncTask的基本用法
AsyncTask类的基本方法:
public abstract class AsyncTask<Params, Progress, Result> {/** * 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); /** * Runs on the UI thread before {@link #doInBackground}. * * @see #onPostExecute * @see #doInBackground */ @MainThread protected void onPreExecute() { } /** * Runs on the UI thread after {@link #doInBackground}. The * specified result is the value returned by {@link #doInBackground}.
* * This method won't be invoked if the task was cancelled.
* * @param result The result of the operation computed by {@link #doInBackground}. * * @see #onPreExecute * @see #doInBackground * @see #onCancelled(Object) */ @SuppressWarnings({"UnusedDeclaration"}) @MainThread protected void onPostExecute(Result result) { } /** * Runs on the UI thread after {@link #publishProgress} is invoked. * The specified values are the values passed to {@link #publishProgress}. * * @param values The values indicating progress. * * @see #publishProgress * @see #doInBackground */ @SuppressWarnings({"UnusedDeclaration"}) @MainThread protected void onProgressUpdate(Progress... values) { }}
从上面AsyncTask的代码可以看到,AsyncTask是一个抽象类,我们如果想要使用的话必须先创建它的子类,并实现它的抽象方法doInBackground,根据功能需要我们还可以重写onPreExecute、onPostExecute、onProgressUpdate。从注释我们就可以知道onPreExecute、onPostExecute、onProgressUpdate这几个方法是运行在main thread,doInBackground是运行在workthread中的。我们把耗时的操作放在doInBackground进行,当doInBackground执行完之后会把结果返回给onPostExecute,我们可以在onPostExecute做一些更新UI的操作。onPreExecute在doInBackground之前执行,用于执行准备工作,onProgressUpdate用来更新后台任务的执行进度。
AsyncTask有三个泛型参数,分别是Params, Progress, Result。Params是指后台任务运行的参数,用于在doInBackground使用,Progress是用来指示后台任务执行进度的单位,Result是后台任务的返回结果,在doInBackground方法中返回,交给onPostExecute处理。这些参数不需要时可以用Void代替。
在本文中我们自定义一个简单的AsyncTask类,代码定义如下:
public class TestAsyncTask extends AsyncTask<Void, Integer, Void> { private String taskName; public TestAsyncTask(String name) { super(); taskName = name; } @Override protected void onPreExecute() { super.onPreExecute(); } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); Log.v("stone", "task = " + taskName + " onPostExecute in " + Thread.currentThread().toString()); } @Override protected Void doInBackground(Void... params) { Log.v("stone", "task = " + taskName + " doInBackground in " + Thread.currentThread().toString()); try { Thread.sleep(10000); } catch (InterruptedException e) { e.toString(); } return null; }}//AsyncTask的两种启动方式new TestAsyncTask("task_" + i).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);new TestAsyncTask("tasktesk_" + i).execute();
到目前为止我我们已经大致了解了AsyncTask的使用,以及启动后台任务的两种方式,execute和executeOnExecutor。在executeOnExecutor中我们使用了AsyncTask自定义的THREAD_POOL_EXECUTOR,它是一个ThreadPoolExecutor。这两种后台任务的启动方式有什么区别呢?一个应用中最多可以new多个AsyncTask呢?多个任务是并行处理还是串行处理呢?最多同时有多少个任务在处理呢?下面我们将从源码的角度来回答上面的问题,由于AsyncTask从出现到现在已经有了多次改动,下面我的分析将是基于API 23的源码来进行的,请各位注意不同API版本的AsyncTask实现是有差异的。
AsyncTask机制详解
通过上文我们已经知道了AsyncTask的简单用法,下面我将从源码的角度来分析,一个AsyncTask从创建到执行的过程。首先一个任何AsyncTask子类的创建都会调用AsyncTask的默认构造函数AsyncTask(),下面我们来看一下这个方法的定义:
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); } } }; }
代码很简单,初始化了mWorker 和mFuture 这两个对象,我们暂时先不管立面的具体实现,只要知道这个mFuture 会交给Executor去执行。现在AsyncTask对象已经创建好了,我们来看AsyncTask的执行过程。我们先来分析execute()方法,该方法的定义如下:
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
execute的代码实际上直接调用了executeOnExecutor方法,并且传入了一个叫做sDefaultExecutor的Executor对象。接着我们来看下executeOnExecutor方法的定义:
public final AsyncTask 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; }
在executeOnExecutor中,我们先调用了onPreExecute()方法,这就解释了为什么onPreExecute方法是最新被调用的。我们把运行参数params赋给mWorker对象,mWorker实现了callable接口。把mFuture交给我们传入的Executor来执行。对面我们前文提到了AsyncTask的两种启动方式,他们执行方式的不同是因为传入的Executor不同导致的。我们前面提到过execute方法传给executeOnExecutor的是一个sDefaultExecutor对象,通过源码来看一下sDefaultExecutor是什么:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
我们看到sDefaultExecutor 被赋值为SERIAL_EXECUTOR,我们来看一下SERIAL_EXECUTOR是如何定义的:
/** * 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();
SERIAL_EXECUTOR 是一个SerialExecutor对象,我们来看一下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); } } }
到这里我们可以知道,通过execute启动AsyncTask任务,会把任务一个一个添加到mTasks中,在执行完一个任务后才会去执行下一个任务,mTasks的大小没有限制,所以理论上通过execute启动AsyncTask任务这种方式可以创建无数个task,并且所有的task是串行执行的。这个runnable参数就是我们在AsyncTask构造函数中初始化的mFuture。我们接着再来看一下构造函数的源码:
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); } } }; }
我们看到在初始化mWorker 对象时,在它的call方法里面调用了doInBackground方法,执行完doInBackground之后会调用postResult(result)来传递doInBackground的返回结果。我们来分析下postResult方法看看结果是如何传递的。
private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); message.sendToTarget(); return result; }
postResult方法中主要是生成了一个MESSAGE_POST_RESULT消息,并将消息发送给了target handler。现在我们就要来看看这个Handler对象是什么,就知道是把消息发送到了哪个线程中。我们来看一下getHandler()方法的实现:
private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } }
看到这里,我们知道这个handler是一个InternalHandler对象,我们接着来看一下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; } } }
看到这里已经很清楚,这个handler是绑定了主线程loop的handler,所以接下来的工作就从工作线程切换到主线程中去执行。在handleMessage中可以看到MESSAGE_POST_RESULT会导致调用result.mTask.finish(result.mData[0])。这个就是AsyncTask的finish方法,我们来看一下finish方法的实现:
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
finish方法里面会做判断,如果 isCancelled()返回的是true,就会去执行onCancelled,否则就会调用onPostExecute方法。至此我们就走完了execute方式的启动流程。通过以上的分析,我们可以知道这种方式下AsyncTask的对象可以创建任意多个并执行,且是顺序执行的。
对于executeOnExecutor启动方式,他的执行方式和传入的Executor相关,每个任务的执行逻辑和execute启动方式是一样的。executeOnExecutor方式中我们传入的参数是AsyncTask.THREAD_POOL_EXECUTOR,这是AsyncTask默认帮我们配置的Executor。我们来具体看下这个Executor的配置情况:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); private static final int CORE_POOL_SIZE = CPU_COUNT + 1; private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE = 1; private static final ThreadFactory sThreadFactory = new ThreadFactory() { private final AtomicInteger mCount = new AtomicInteger(1); public Thread newThread(Runnable r) { return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); } }; private static final BlockingQueue sPoolWorkQueue = new LinkedBlockingQueue(128); /** * 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);
这个ThreadPoolExecutor配置如下:核心线程数为CPU核个数+1,最大线程数是2倍的CPU核个数+1,任务排队队列大小为128,所以假设我们的手机CPU核的个数为8,在核心线程数为9,最大线程数为17。所以根据ThreadPoolExecutor的特点我们可以知道,最大的任务数为128+17,超过这个数量就会抛出RejectedExecutionException。在排队队列未满之前,最多有9个线程在运行,当排队队列满了之后,最多有17个线程在运行。
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- Android面试题收集
- Android(安卓)SQLite系列
- Android(安卓)WebView 使用漏洞
- AndroidManifest.xml文件剖析 (二)
- Android(安卓)X库 BiometricPrompt 中 Crypto primitive not ini
- android关闭应用