进阶 | AsyncTask 源码浅析

进阶 | AsyncTask 源码浅析
文章图片

AsyncTask方便我们从异步工作切换到UI线程。虽然是异步的但是依然不建议让AsyncTask执行过大耗时操作(超过10秒),如果需要让一个线程长时间保持运行,请用线程池。AsyncTask有三个泛型四个方法需要注意。
简单介绍 三个泛型:Params用于初始化,Progress用于设置进度,Result返回的结果,如果没有参数则设置为Void
四个方法:

  1. onPreExecute(),任务执行之前操作
  2. doInbackground(Params),后台线程执行
  3. onProgressUpdate(Progress),任务执行过程中,主线程
  4. onPostExecute(),任务执行完成
  5. 补充, onCancelled(),取消任务
简单使用:
class DownloadTask extends AsyncTask { @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(); } } }

源码 【进阶 | AsyncTask 源码浅析】两个线程池:
  1. SerialExecutor :任务排队
  2. THREAD_POOL_EXECUTOR:执行任务
注意点:
  1. AsyncTask的类加载必须在主线程中完成,因为AsyncTask类中有静态域sHandler,用于切换主线程和子线程。sHnadler必须获取主线程的Looper,所以sHandler必须在主线程中创建。而静态变量的创建在类加载的时候完成,所以AsyncTask必须在主线程中加载。Android4.1之后系统自动完成这个操作。
  2. AsyncTask对象创建和execute方法必须在主线程中调用
    进阶 | AsyncTask 源码浅析
    文章图片

    但是为啥呢?大都数帖子都说,因为sHandler必须在主线程中创建,但是sHandler为静态变量,在类加载的时候就创建了,而不是在对象生成的时候创建。这个解释是错误的。如果您知道为啥,还请指点一二。
  3. 一个AsyncTask只能被执行一次,不然会被报异常。查看源码,在执行 executor()方法之后,首先会检测AsyncTask的Status,如果 Status为RUNDING或者FINISHED则会抛出异常。在检测完Status之后将其设置为RUNNING
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; }

  1. Android3.0 之后AsyncTask任务由一个线程串行执行,如果是调用executeOnExecute则是并行执行任务。查看源码
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); // 执行任务 } } }

初始状态mActive为null,调用scheduleNext(),出队列让THREAD_POOL_EXECUTOR线程池去执行任务。r.run()执行完成之后运行finaly{}语句,调用下一个任务,依次继续。
参考资料
  • 《Android艺术探索》
  • android中handler的一些总结以及使用(三)之HandleThread的使用

    推荐阅读