AsyncTask源码分析
2016-01-29 00:40
281 查看
AsyncTask的使用介绍:
AsyncTask从Build.VERSION_CODES#HONEYCOMB开始默认的执行方式是单个线程串行执行任务,而不是多个线程并发执行,如果要多个线程并发执行任务,可以调用executeOnExecutor()方法,并传入其自带的THREAD_POOL_EXECUTOR字段,该类异步操作适合较短的耗时操作,官方例子如下:
[code]/**泛型分别是代表oInBackgroud(),onProgressUpdate(),onPostExecute()三个方法的参数类型*/ private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> { //一定要重写该方法,该方法在子线程执行耗时操作 protected Long doInBackground(URL... urls) { int count = urls.length; long totalSize = 0; for (int i = 0; i < count; i++) { totalSize += Downloader.downloadFile(urls[i]); publishProgress((int) ((i / (float) count) * 100)); // Escape early if cancel() is called if (isCancelled()) break; } return totalSize; } //在UI线程执行,以更新进度条 protected void onProgressUpdate(Integer... progress) { setProgressPercent(progress[0]); } //doInBackground执行完后,结果会以参数形式传入,并调用其方法 protected void onPostExecute(Long result) { showDialog("Downloaded " + result + " bytes"); } }
AsyncTask的源码剖析:
AsyncTask的构造方法:[code]/** * Creates a new asynchronous task. This constructor must be invoked on the UI thread. */ public AsyncTask() { //任务对象 mWorker = new WorkerRunnable<Params, Result>() { //AsyncTask对象调用execute()时候,实际会来到这里开始执行耗时任务 public Result call() throws Exception { //任务被执行的标记,任务如果被执行,则为true mTaskInvoked.set(true); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //执行耗时的后台任务 Result result = doInBackground(mParams); Binder.flushPendingCommands(); return postResult(result); } }; mFuture = new FutureTask<Result>(mWorker) { //上面的mWorker对象的call()执行完时候会执行该方法 @Override protected void done() { try { //当任务没有被call方法执行时,也会调用postResult方法间接运行onPostExecute()方法 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); } } }; }
从构造方法可以看出,每一个任务将会被封装成WorkerRunnable对象,然后再封装成FutureTask对象,那么WorkerRunnable对象又是什么呢?它是一个实现Callable的子类,代码如下:
[code] private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> { //传给doInBackgroud的参数值 Params[] mParams; }
关于FutureTask类是什么东西,可以参考下面我之前写过关于线程一篇博客,那里有讲到它的用法:
/article/1365426.html
AsyncTask.execute()的方法分析:
[code]//一定要在主线程调用,并且默认是单个线程串行执行任务 @MainThread public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); } //调用execute()实际上在调用executeOnExecutor()方法,并且只能调用一次 @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(); //设置传给doInBackgroud的参数 mWorker.mParams = params; //执行任务 exec.execute(mFuture); return this; }
从源码分析可以看出AsyncTask.execute()一定要在主线程执行并且只能执行一次,然后再通过调用
exec.execute(mFuture)执行任务,该方法一旦调用,会自动执行mFuture里面的WorkRunnable的call()方法,这就意味着doInBackgroud()方法也将在调用exec.execute(mFuture)方法后执行,而
exec是在由变量sDefaultExecutor传入,而sDefaultExecutor又是在初始时候赋值的,看下面源码分析:
[code]//串行执行任务的执行器 public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //初始化线程执行器 private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; //获取CPU核数 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<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(128); //执行任务线程池 public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); //自定义串行执行任务的执行器 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(); } } }); //第一次执行该execute()的方法时,会执行这里 if (mActive == null) { scheduleNext(); } } /**同步执行下一个任务*/ protected synchronized void scheduleNext() { if ((mActive = mTasks.poll()) != null) { THREAD_POOL_EXECUTOR.execute(mActive); } } }
从源码可以知道有两个细节问题,第一个就是任务是通过队列来管理的,这也说明默认AsyncTask执行任务方式是采用先进先出的方式来逐一执行的,第二个就是AsyncTask的所有任务实际上是由THREAD_POOL_EXECUTOR这个线程池来执行的,然而其存储方式是链表阻塞队列,并且初始化容量是128 Runnable对象,这也意味着,如果用AsyncTask采用THREAD_POOL_EXECUTOR这个线程并发执行任务,则任务对象存储不能超过128,但串行执行任务就不用考虑其问题,因为其采用同步方式,保证任何时刻只有一个任务在THREAD_POOL_EXECUTOR这个线程池中。
AsyncTask的onPostExecute()和onProgressUpdate()方法调用时刻:
从上面AsyncTask()构造方法,我们知道call()方法里面调用了postResult(result)方法,done()方法里又执行了postResultIfNotInvoked()方法,那么这两个方法做了什么呢?看下面源码分析:
[code]//存储异步任务得到的结果信息类 private static class AsyncTaskResult<Data> { final AsyncTask mTask; final Data[] mData; AsyncTaskResult(AsyncTask task, Data... data) { mTask = task; mData = data; } } /**结果传送给handler处理*/ private Result postResult(Result result) { @SuppressWarnings("unchecked") Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult<Result>(this, result)); message.sendToTarget(); return result; } /**如果任务没有被call()执行,也会调用postResult()方法,间接执行onPostExecute()方法*/ private void postResultIfNotInvoked(Result result) { final boolean wasTaskInvoked = mTaskInvoked.get(); if (!wasTaskInvoked) { postResult(result); } } @WorkerThread /**更新进度条*/ protected final void publishProgress(Progress... values) { if (!isCancelled()) { getHandler().obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult<Progress>(this, values)).sendToTarget(); } }
源码那里可以知道,更新UI操作是交给了handler去处理,那么handler是如何处理呢?看下面源码分析:
[code]//获得handler private static Handler getHandler() { synchronized (AsyncTask.class) { if (sHandler == null) { sHandler = new InternalHandler(); } return sHandler; } } //定义静态内部handler以避免内存泄漏 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 //里面会执行onPostExecute()方法 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()方法 onPostExecute(result); } mStatus = Status.FINISHED; }
从源码分析,可以看出ononProgressUpdate()和onPostExecute()方法都是在handler当中执行,故可以更新UI操作。
AsyncTask的取消(cancel()方法)任务分析:
[code]/**取消任务执行*/ public final boolean cancel(boolean mayInterruptIfRunning) { //取消后续的任务执行 mCancelled.set(true); //是否当断当前任务执行 return mFuture.cancel(mayInterruptIfRunning); } public final boolean isCancelled() { return mCancelled.get(); }
再结合上面的finish()源码,我们知道一旦调用了cancel()方法,那么后面(包括当前正在执行的任务)的任务,其onPostExecute()方法将不会被执行,而如果cancel()传入了true,那么当前执行的任务会被打断,这也意味着doInBackgroud()可能没有完成就被打断,而传入false时候,则会将当前执行任务中的doInBackgroud()方法执行完。
AsyncTask的总结:
综合上面的所有分析知道,AsyncTask.execute()实际上是先通过executeOnExecutor()方法调用了onPreExecute()方法,然后SerialExecutor.execute()以数组队列的方式去存储任务(默认单个线程串行执行任务时候),最后通过THREAD_POOL_EXECUTOR线程池调用execute()去执行真正的任务,一旦任务开始启动执行就会执行WorkRunnable中的call方法,而call又会执行doInBackgroud()方法执行耗时操作,如果里面有publishProgress()方法将会通过主线程handler的方式发送消息,去调用onProgressUpdate()方法更新进度条,当doInBackgroud()方法执行完后,会通过调用postResult()方法发送消息,以让主线程handler通过调用finshed()方法里面的onPostExecute()去更新UI或处理结果。如果该任务没有被执行,则也会通过postResultIfNotInvoked()方法调用postResult()方法,从而间接执行onPostExecute()方法。
相关文章推荐
- [工作bug]c:import参数传递问题解析
- 编程题1001.A+B Format (20)
- UIAlertController和UIAlertAction的简单使用
- jQuery实现跨域请求
- 使用LaTeX beamer文档类时warning: Option ‘pdfauthor’ has already been used
- POJ 2376 Cleaning Shifts
- POJ2186(有向图缩点)
- poj1681 高斯消元
- poj1681 高斯消元
- JAVA编译时与运行时
- 边框和背景
- java数组实现栈
- 使用LaTeX beamer文档类时warning: ``mathserif” isobsolete
- Java初学者1-Java环境及IDE
- 【Java SE】————如何做到跨平台性的?
- 上下滚动字幕
- 使用LaTeX beamer文档类时错误:\trans@languagepath ->\languagename
- 1到整数n中 出现1的次数,分,最高位,非最高位,两次数相加。递归位数减1.
- poj 1830 高斯消元
- poj 1830 高斯消元