详解AsyncTask实现原理
2017-02-25 21:07
127 查看
AsyncTask允许执行后台计算并将计算结果发送给UI线程,过程中无需显示地借助Handler等。这是因为AsyncTask内部封装了线程池用于执行后台计算,并通过Handler将结果传递给UI线程。
根据下面AsyncTask的定义可知,主要包含三个参数,分别表示:参数类型、后台任务执行的进度类型、返回的结果类型。若不需要某个参数,可直接设为Void类型。
AsyncTask的使用步骤如下:
1)定义一个AsyncTask的子类;
子类中需要实现以下几个函数:
onPreExecute():执行在UI线程中,execute(Params... params)被调用后立即执行,主要用来在执行后台任务前对UI作一些标记,如启动一个进度条等。
doInBackGround(Params... params):在onPreExecute()执行完成后立即执行,用于执行后台任务,在该方法中可调用publishProgress(Progress... progress)来更新进度信息。
onProgressUpdate(Progress... progress):执行在UI线程中,publishProgress方法执行后将触发本方法执行,用以将进度信息更新到UI组件上。
onPostExecute(Result result):执行在UI线程中。当后台任务执行完成后,将触发被方法的调用,接收doInBackground方法返回的计算结果。
2)在UI线程中创建AsyncTask子类的实例;
3)在UI线程中调用AsyncTask子类实例的execute(Params... params)方法
注意,一个异步任务的实例只能执行一次,否则会抛出异常。这是因为异步任务执行时会检查其状态,若为已执行则抛出异常,我们会在下面的源码讲解中指出。
我们通过调用execute(Params... params)来开启异步任务,那么这个方法调用背后的原理是什么呢?接下来我们通过学习AsyncTask的源码来探讨AsyncTask的实现原理。
根据源码可知,execute方法仅仅是调用了executeOnExecutor方法。我们接着查看executeOnExecutor方法,可以看出该方法中首先对异步任务的状态进行检查,若状态为RUNNING、FINISHED则抛出相应的异常,否则设当前异步任务的状态为RUNNING,并调用onPreExecute方法对UI做一些标记,然后将execute方法传入的参数传递给mWorker,并通过线程池sDefaultExecutor执行任务。我们确实可以看出,execute和onPreExecute方法都是执行在UI线程中的。
exec.execute(mFuture)究竟做了什么呢?我们继续查看sDefaultExecutor和参数mFuture。从下面源码可以看出,sDefaultExecutor是SerialExecutor的实例,而SerialExecutor实现了Executor接口,是一个顺序执行任务的线程池,其主要作用是管理提交的任务,最终将任务交给THREAD_POOL_THREAD线程池执行。mFuture是FutureTask类型实例。
THREAD_POOL_EXECUTOR是一个ThereadPoolThread类型的静态不可变实例,根据下面的源码可知,其核心线程数最小为2,最大为4,最大线程数为(CPU内核数*2+1),任务队列基于LinkedBlockingQueue实现,最大容量为128。
mWorker和mFuture都是在AsyncTask的构造函数中实例化的。mWorker是WorkerRunnable类型的实例,WorkerRunnable实现了Callable接口,并定义了一个Params[] params来接受AsyncTask的execute(Params... params)传入的参数。mWorker的实例化过程中调用了doInBackground函数,并将任务的执行结果传递给postResult(Result
result)方法,并执行postResult方法。mWorker封装了后台任务,mFuture根据mWorker创建FutureTask实例,mFuture运行时将执行mWorker的call方法。
根据上述postResult的源码可以看出,它的作用是将当前AsyncTask实例和任务的执行结果封装在一个AsyncTaskResult类型对象中,并通过InternalHandler类型的sHandler传递给UI线程。InternalHandler用以处理异步任务的线程池传递过来的任务的执行消息,包括执行进度和执行结果。可以看出,若消息是执行进度,则调用当前AsyncTask实例的onPrograssUpdate方法更新UI线程中的进度信息显示;若消息是执行结果,则调用当前AsyncTask实例的finish方法。
那么AsyncTask的finish究竟做了什么呢?从源码可以看出,在finish方法中对任务执行结果的返回进行类型判断,若是取消任务并返回则调用onCancelled方法;若是任务执行完成并返回则调用onPostExecute方法(可以看出执行在UI线程中)。最后,finish方法将异步任务的状态置为FINISHED。
根据下面AsyncTask的定义可知,主要包含三个参数,分别表示:参数类型、后台任务执行的进度类型、返回的结果类型。若不需要某个参数,可直接设为Void类型。
public abstract class AsyncTask<Params, Progress, Result>
AsyncTask的使用步骤如下:
1)定义一个AsyncTask的子类;
子类中需要实现以下几个函数:
onPreExecute():执行在UI线程中,execute(Params... params)被调用后立即执行,主要用来在执行后台任务前对UI作一些标记,如启动一个进度条等。
doInBackGround(Params... params):在onPreExecute()执行完成后立即执行,用于执行后台任务,在该方法中可调用publishProgress(Progress... progress)来更新进度信息。
onProgressUpdate(Progress... progress):执行在UI线程中,publishProgress方法执行后将触发本方法执行,用以将进度信息更新到UI组件上。
onPostExecute(Result result):执行在UI线程中。当后台任务执行完成后,将触发被方法的调用,接收doInBackground方法返回的计算结果。
2)在UI线程中创建AsyncTask子类的实例;
3)在UI线程中调用AsyncTask子类实例的execute(Params... params)方法
注意,一个异步任务的实例只能执行一次,否则会抛出异常。这是因为异步任务执行时会检查其状态,若为已执行则抛出异常,我们会在下面的源码讲解中指出。
我们通过调用execute(Params... params)来开启异步任务,那么这个方法调用背后的原理是什么呢?接下来我们通过学习AsyncTask的源码来探讨AsyncTask的实现原理。
根据源码可知,execute方法仅仅是调用了executeOnExecutor方法。我们接着查看executeOnExecutor方法,可以看出该方法中首先对异步任务的状态进行检查,若状态为RUNNING、FINISHED则抛出相应的异常,否则设当前异步任务的状态为RUNNING,并调用onPreExecute方法对UI做一些标记,然后将execute方法传入的参数传递给mWorker,并通过线程池sDefaultExecutor执行任务。我们确实可以看出,execute和onPreExecute方法都是执行在UI线程中的。
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
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)"); } }//执行前会判断AsyncTask的状态,这也是AsyncTask实例只能执行一次的原因。 mStatus = Status.RUNNING;//将AsyncTask的状态置为正在运行 onPreExecute();//调用此函数,在UI线程中对UI做一些标记,如开启一个进度条等 mWorker.mParams = params; exec.execute(mFuture);//!!!线程池exec执行任务!!!execute函数传递过来的形参exec是线程池sDefaultExecutor return this; }
exec.execute(mFuture)究竟做了什么呢?我们继续查看sDefaultExecutor和参数mFuture。从下面源码可以看出,sDefaultExecutor是SerialExecutor的实例,而SerialExecutor实现了Executor接口,是一个顺序执行任务的线程池,其主要作用是管理提交的任务,最终将任务交给THREAD_POOL_THREAD线程池执行。mFuture是FutureTask类型实例。
private static volatile Executor sDefaultExecutor = 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();//一个顺序执行任务的线程池
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) {//取出双端队列mTasks的队头元素,赋值给mActive THREAD_POOL_EXECUTOR.execute(mActive);//!!!最终执行任务的是THREAD_POOL_EXECUTOR线程池!!! } } }
THREAD_POOL_EXECUTOR是一个ThereadPoolThread类型的静态不可变实例,根据下面的源码可知,其核心线程数最小为2,最大为4,最大线程数为(CPU内核数*2+1),任务队列基于LinkedBlockingQueue实现,最大容量为128。
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors(); // We want at least 2 threads and at most 4 threads in the core pool, // preferring to have 1 less than the CPU count to avoid saturating // the CPU with background work private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4)); private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1; private static final int KEEP_ALIVE_SECONDS = 30;
/** * 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; }
mWorker和mFuture都是在AsyncTask的构造函数中实例化的。mWorker是WorkerRunnable类型的实例,WorkerRunnable实现了Callable接口,并定义了一个Params[] params来接受AsyncTask的execute(Params... params)传入的参数。mWorker的实例化过程中调用了doInBackground函数,并将任务的执行结果传递给postResult(Result
result)方法,并执行postResult方法。mWorker封装了后台任务,mFuture根据mWorker创建FutureTask实例,mFuture运行时将执行mWorker的call方法。
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { Params[] mParams; }
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) {//提交到线程池的任务,根据传入的mWorer创建实例,将在运行时执行mWorker的call()方法 @Override protected void done() { try { postResultIfNotInvoked(get());//确保postResult一定会被调用。若任务未执行,则该方法将调用postResult } 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 void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get();//任务是否执行的标志变量 if (!wasTaskInvoked) { postResult(result);//若任务未执行,mWorker中的postResult就不会被调用,因此需主动调用通知UI线程 } } private Result postResult(Result result) {//将任务的执行结果发送给handler,通过handler将执行结果发送给UI线程 @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result));//getHandler()返回sHandler(InternalHandler的实例) message.sendToTarget(); return result; }
根据上述postResult的源码可以看出,它的作用是将当前AsyncTask实例和任务的执行结果封装在一个AsyncTaskResult类型对象中,并通过InternalHandler类型的sHandler传递给UI线程。InternalHandler用以处理异步任务的线程池传递过来的任务的执行消息,包括执行进度和执行结果。可以看出,若消息是执行进度,则调用当前AsyncTask实例的onPrograssUpdate方法更新UI线程中的进度信息显示;若消息是执行结果,则调用当前AsyncTask实例的finish方法。
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 static class AsyncTaskResult<Data> {//包装了当前AsyncTask实例和任务执行结果 final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } }
那么AsyncTask的finish究竟做了什么呢?从源码可以看出,在finish方法中对任务执行结果的返回进行类型判断,若是取消任务并返回则调用onCancelled方法;若是任务执行完成并返回则调用onPostExecute方法(可以看出执行在UI线程中)。最后,finish方法将异步任务的状态置为FINISHED。
private void finish(Result result) { if (isCancelled()) { onCancelled(result); } else { onPostExecute(result); } mStatus = Status.FINISHED; }
相关文章推荐
- Linux0.11内核--系统中断处理程序int 0x80实现原理
- Spring AOP 实现原理与 CGLIB 应用
- 储水池抽样算法原理与实现
- 数据库连接池原理详解与自定义连接池实现
- paip.编程语言方法重载实现的原理及python,php,js中实现方法重载
- PCA算法人脸识别小结--原理到实现
- JDK 动态代理实现及原理解析
- C语言中可变参数函数实现原理
- 内存缓存 原理 实现
- hibernate ORM实现原理
- C#获取远程网页中的所有链接URL(网络蜘蛛实现原理)
- IOS: 状态栏提示控件的实现原理UIWindowLevel详解关于UIWindow的一点儿思考
- Android 四大组件 Service实现原理以及AIDL语言的使用详解
- JS 实现无缝滚动动画原理(初学者入)
- Java中HashMap实现原理初探
- Java JDK 动态代理使用及实现原理分析
- 机器学习算法 原理、实现与实战——模型评估与模型选择
- 数据库水平切分的实现原理解析-分…
- Java服务器热部署的实现原理
- 《MonkeyRunner 原理剖析》第九章-MonkeyImage 实现原理 - 第三节 - 裸图 RawImage