Android多线程(一)AsyncTask源码原理解析

  最近在review去年处理过的bug,发现一个关于AsyncTask的问题,反正最近也在review代码,那就好好学习一下Android开发中经常用到的线程相关知识吧。前面说的问题是这样的,相册在读取连拍照片时,会有一段时间的黑屏,但是照片的数量并不多,只有20张,使用Asynctask加载的话应该可以秒开才对,看了一下代码:

private AsyncTask<Void,Void,Void> mInitBurstDataTask = new AsyncTask<Void, Void, Void>() {

    @Override  
    protected void onPreExecute() {  
        mDialog.show();  
    }  

    @Override
    protected Void doInBackground(Void... voids) {
        if(mBurstShotIdList != null) {
            initBurstData(mBurstShotIdList);
        }
        return null;
    }

    @Override  
    protected void onProgressUpdate(Integer... values) {  
        showProgressBar(); 
    }  

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        initAdapter();
    }
};
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.burstshot_main);
    mInitBurstDataTask.execute();
}

  上面的代码基本也是AsyncTask的通常用法,AsyncTask提供的四个核心方法,分别是:
1、onPreExecute()
工作在UI线程中,先于异步任务doInBackground()执行,例如界面的初始化工作;
2、doInBackground(Params… params)
工作在子线程中,处理耗时的任务,params是异步任务需要的参数,此时可以通过publishProgress(Progress… values)方法来更新任务的进度;
3、onProgressUpdate(Progress… values)
在主线程中执行,当在后台任务中调用了publishProgress(Progress… values)方法后,这个方法就会被调用,方法中携带的参数就是在后台任务中传递过来的;
4、onPostExecute(Result result)
当异步任务执行完之后在主线程中执行,result是异步任务结束后的返回值。
现在来看一下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);
              }
          }
      };
  }

  在AsyncTask构造函数中,初始化了两个变量,mWorker和mFuture,分别是Callable和FutureTask对象,也就是说我们new一个AsyncTask出来的时候,并没有开始执行什么具体的操作,我们知道,AsyncTask开始异步任务是从execute()开始的,来看一下这个方法的具体实现:

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}

仅仅是调用了executeOnExecutor(sDefaultExecutor, params)并出入了sDefaultExecutor和params两个参数,接着我们看一下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();

      mWorker.mParams = params;
      exec.execute(mFuture);

      return this;
  }

  从上面的代码中可以看到, onPreExecute()是最先执行的,然后把传进来的params参数赋给了mWorker,然后把mFutures传入sDefaultExecutor的execute()方法执行,至此,构造函数中初始化的两个变量都用到了。这时又转到了sDefaultExecutor上面来了,来看看它的定义:

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
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) {
              THREAD_POOL_EXECUTOR.execute(mActive);
          }
      }
  }

  从上面的代码看出,sDefaultExecutor内部维护了一个双端队列mTasks,通过 mTasks.offer(Runnable)将传进来的mFuture(FutureTask实现了Runnable接口)插入队列中,从这里可以看出默认情况下AsyncTask的异步任务是串行执行的,我们从头梳理一下,传入的params赋值到mWorker,然后mWorker又被转入mFutureTask中去,然后将mFutureTask又被插入队列中,也就是说,params参数最终被封装称了FutureTask对象,而FutureTask是一个并发类,所以AsyncTask的异步任务是在FutureTask中得到执行的。上面代码的11行调用了mFutureTask的run()方法,这个方法中又会执行mWorker的call方法:

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);
}

在mWorker的call()方法中,首先将mTaskInvoked设置为ture,表示当前任务已经被执行过了,接着执行AsyncTask中的doInBackground(mParams),然后通过postResult(result)把异步任务的结果发送出去,来看一下postResult()的源码:

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

  这里用到了sHandler来发送一个MESSAGE_POST_RESULT消息,接着看一下sHandler的具体实现:

private static InternalHandler 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;
        }
    }
}

sHandler收到了MESSAGE_POST_RESULT之后调用了AsyncTask的finish()方法:

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

  在这里,首先判断AsyncTask是否被取消了,即是否调用了cancel(boolean mayInterruptIfRunning)这个方法,如果AsyncTask被取消了就调用onCancelled()否则调用onPostExecute(),可以看到doInbackground()异步任务的结果通过Handler传回到了主线程的onPostExecute()中,这样AsyncTask的一个异步任务流程就这样走完了。回头看一下InternalHandler中还有这样一个消息MESSAGE_POST_PROGRESS,不用解释,调用的正是onProgressUpdate()方法。
在前面说了,在默认情况下AsyncTask的异步任务是串行执行的,同一进程中同时开启了多个AsyncTask的话,如果前面的任务没有执行完成,后面的任务就会被阻塞,有没有办法可以让提交的任务马上执行呢?答案当然是肯定的,那就是调用AsyncTask的Task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)方法,THREAD_POOL_EXECUTOR是AsyncTask内部维护的静态线程池:

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;

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);

/**
 * 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;
}

  AsyncTask中有两个线程池,SERIAL_EXECUTOR和THREAD_POOL_EXECUTOR,其中SERIAL_EXECUTOR是用于任务排队的,THREAD_POOL_EXECUTOR才是真正执行线程任务的。在我们调用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)方法后,如果线程池中还有空闲的线程,我们的任务就会得到并行处理,此外我们还可以定义自己的线程池,这样就可以更好的控制线程的执行。
  不要忘了,我们的bug还没有处理,在我们的代码中采用的是AsyncTask默认的任务执行方式(串行提交),现在采用并行的方式:

mInitBurstDataTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

重新编译apk,推入手机,果然可以到达秒开的预期效果。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值