AsyncTask使用与源码解析

Posted 单灿灿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AsyncTask使用与源码解析相关的知识,希望对你有一定的参考价值。

本文承接于AsyncTask使用与源码解析

下面我们来看一下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();//scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。
            
        

        protected synchronized void scheduleNext() 
            if ((mActive = mTasks.poll()) != null) 
                THREAD_POOL_EXECUTOR.execute(mActive);
            
        
    

当有任务到来时, mTasks.offer就把任务入队,然后在scheduleNext中出队一个任务,然后放到THREAD_POOL_EXECUTOR里去执行,虽然是放到THREAD_POOL_EXECUTOR去执行了,可是一次只放一个任务,因此是串行执行的,所以这个SerialExecutor的作用就是将任务排好队,一个一个放入线程池中去执行,所以我们直接调用execute开启一个AsyncTask的时候,任务是串行执行的。

SerialExecutor类中也有一个execute()方法,这个方法里的所有逻辑就是在子线程中执行的了,注意这个方法有一个Runnable参数,这个参数的值是什么呢?是mFuture对象,在r.run();我们要调用的是FutureTask类的run()方法,而在这个方法里又会去调用Sync内部类的innerRun()方法,我们来看innerRun()方法。

SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,

如果我们一次性启动了很多个任务,首先在第一次运行execute()方法的时候,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,

然后判断mActive对象是不是等于null,第一次运行当然是等于null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。

那么后面添加的任务岂不是永远得不到处理了?

当然不是,看一看offer()方法里传入的Runnable匿名类,这里使用了一个try finally代码块,

并在finally中调用了scheduleNext()方法,保证无论发生什么情况,这个方法都会被调用。也就是说,每次当一个任务执行完毕后,下一个任务才会得到执行,

SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。

    //看郭林的代码的时候找不到此段
void innerRun()   
    if (!compareAndSetState(READY, RUNNING))  
        return;  
    runner = Thread.currentThread();  
    if (getState() == RUNNING)  // recheck after setting thread  
        V result;  
        try   
            result = callable.call();  
         catch (Throwable ex)   
            setException(ex);  
            return;  
          
        set(result);  
     else   
        releaseShared(0); // cancel  
      
  

result = callable.call();调用了callable的call()方法,那么这个callable对象是什么呢?其实就是在初始化mFuture对象时传入的mWorker对象了,此时调用的call()方法,也就是一开始在AsyncTask的构造函数中指定的,我们来看一下代码:

public Result call() throws Exception   
    mTaskInvoked.set(true);  
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
    return postResult(doInBackground(mParams));  
  

在postResult()方法的参数里面,我们找到了doInBackground()方法的调用处,经过了很多周转,目前的代码仍然是运行在子线程当中的,所以这也就是为什么我们可以在doInBackground()方法中去处理耗时的逻辑。接着将doInBackground()方法返回的结果传递给了postResult()方法。

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

    private static Handler getHandler() 
        synchronized (AsyncTask.class) 
            if (sHandler == null) 
                sHandler = new InternalHandler();
            
            return sHandler;
        
    

熟悉了异步消息处理机制,这段代码一定非常简单吧。(觉得不简单的,点击进入)这里使用sHandler对象发出了一条消息,消息中携带了MESSAGE_POST_RESULT常量和一个表示任务执行结果的AsyncTaskResult对象。

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;
            
        
    

消息的类型进行了判断,如果这是一条MESSAGE_POST_RESULT消息,就会去执行finish()方法,如果这是一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。

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

InternalHandler的handleMessage()方法里,还有一种MESSAGE_POST_PROGRESS的消息类型,这种消息是用于当前进度的,调用的正是onProgressUpdate()方法,那么什么时候才会发出这样一条消息?

  @WorkerThread
    protected final void publishProgress(Progress... values) 
        if (!isCancelled()) 
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        
    

在doInBackground()方法中调用publishProgress()方法才可以从子线程切换到UI线程,从而完成对UI元素的更新操作。

说到底,AsyncTask也是使用的异步消息处理机制,只是做了非常好的封装而已。
读到这里,相信你对AsyncTask中的每个回调方法的作用、原理。

AsyncTask有什么缺陷?

工作线程是不能更新UI,同一个AsyncTask任务只能执行一次。最大支持128个线程的并发,10个任务的等待。

至此,AsyncTask源码分析完毕,希望您对AsyncTask有了更深的理解

源码下载地址,点击下载

以上是关于AsyncTask使用与源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Android -- AsyncTask源码解析

AsyncTask使用与源码解析

AsyncTask使用与源码解析

android源码解析之-->异步任务AsyncTask

AsyncTask源码解析

AsyncTask源码解析