Android-AsyncTask讲解

AsyncTask就是安卓中一个对线程池的典型的应用,其中还涉及了之前博文中介绍的FutureTask等知识点,本文主要对AsyncTask源码做一次讲解

官方介绍

AnsycTask官方介绍.png

上面截图就是官方对AsyncTask的介绍,内容比较多就不逐字逐句翻译了,挑几个重要的解释一下。

  1. AnsycTask主要目的是执行短时间的操作,其他的任务更推荐使用线程池
  2. AnsycTask定义了后台计算的方法,同时结果更新在UI线程,定义了Params、Progress、Result分别用于表示执行参数、进度参数以及结果,不需要某个参数时可以用Viod。
  3. 使用过程相关的方法主要有如下几个:
  • onPreExecute():主要用于一些计算前的数据准备,在UI线程被调用
  • doInBackground(Params...):后台计算的方法,子线程中调用,一般在此方法调用publishProgress(Progress... values)用于更新进度等操作,在子线程调用
  • onProgressUpdate(Progress...):后台操作进度的更新到UI上,在UI线程中调用
  • onPostExecute(Result):计算结果的返回,在UI线程调用
  1. AnsycTask执行过程中可以取消,取消的形式和FutureTask一致
  2. AnsycTask实例只能在UI线程被创建,同时execute(Params...) 也只能在UI线程被调用,并且只能被调用一次,多次调用抛出异常。
  3. 多个AnsycTask实例对象分别调用execute(Params...)也是被同步执行的,这点比较坑,因为内部用的默认线程池是静态定义的,并且是同步的
  4. 调用AnsycTask的静态方法 execute (Runnable runnable),多个runnable也是同步执行的
  5. 调用AnsycTask的成员方法executeOnExecutor (Executor exec, Params... params)可指定任务执行的线程池,一般推荐调用系统默认实现的THREAD_POOL_EXECUTOR线程池。这样可以多个实例并行执行。

上面差不多就是AnsycTask的重点了下面就源码做一次解析。

先从线程池开始说起,与多线程相关的主要就是THREAD_POOL_EXECUTOR和sDefaultExecutor这个两个线程池。

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

之前博文中有介绍的,这个构建出来的线程池拥有的核心线程数是CORE_POOL_SIZE,最大线程数是MAXIMUM_POOL_SIZE,任务队列是sPoolWorkQueue,线程构建通过sThreadFactory,非核心线程存活时间为KEEP_ALIVE_SECONDS

核心线程的计算如下:

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

最多为cpu核心的个数-1个。

任务队列构建如下:

private static final BlockingQueue sPoolWorkQueue =        
        new LinkedBlockingQueue(128);                      

可以看到最多为128个任务。

SerialExecutor

这个就是不指定线程池时任务默认处理用的线程池,来看一下里面的实现

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

内部很简单,将传递进来的任务保存在新的Runnable中并入队,等待被执行。

  1. 这个执行的过程时通过scheduleNext()来控制的,可以看出队列里任务都是等上一个任务执行完毕后再执行下一个任务实现了串行的效果。
  2. SerialExecutor实例对象在AsyncTask中以静态变量的形式存在,这意味着所有的子类共用这个默认的线程池,这也是为什么开头说的多个子类实例执行也是串行的原因所在。

构造方法

AsyncTask开始构造时,会初始化两个成员变量mWorker和mFuture,

  1. mWorker将耗时操作放在此执行,并将结果传递下去
  2. mFuture包装mWorker,传递给线程池执行,并处理结果

任务执行状态的区分主要靠这两个变量,我们来看一下源码

public AsyncTask() { 
    
    //其实是一个Callable                                                                           
    mWorker = new WorkerRunnable() {                                            
        public Result call() throws Exception {                                                 
            mTaskInvoked.set(true);                                                             
            Result result = null;                                                               
            try {                                                                               
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                  
                //noinspection unchecked                                                        
                result = doInBackground(mParams);                                               
                Binder.flushPendingCommands();                                                  
            } catch (Throwable tr) {                                                            
                mCancelled.set(true);                                                           
                throw tr;                                                                       
            } finally {                                                                         
                postResult(result);                                                             
            }                                                                                   
            return result;                                                                      
        }                                                                                       
    };                                                                                          
                                                                                                
    mFuture = new FutureTask(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);                                                   
            }                                                                                   
        }                                                                                       
    };                                                                                          
}                                                                                               
 
 private void postResultIfNotInvoked(Result result) {                 
     final boolean wasTaskInvoked = mTaskInvoked.get();               
     if (!wasTaskInvoked) {                                           
         postResult(result);                                          
     }                                                                
 }                                                                    
                                                                      
 private Result postResult(Result result) {                           
     @SuppressWarnings("unchecked")                                   
     Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
             new AsyncTaskResult(this, result));              
     message.sendToTarget();                                          
     return result;                                                   
 }                                                                                                                                                                                                           

WorkerRunnable本质上是一个Callable,配合下面的FutureTask,可以让线程池执行之后获取对应的结果。

上面的代码也可以看到一点点端倪,看AsyncTask是怎么执行代码的:

  1. mWorker开始的时候就mTaskInvoked状态置为了true(防止结果无法被处理)同时在当前线程调用了doInBackground(),保存了结果。
  2. 在将结果往下传递的过程中先判断了任务是否被中断,若任务被中断,则mCancelled置为true。
  3. mFuture获取结果,通过不同的状态讲结果处理。

任务的开启

上面说了任务的构造,这里说说任务的开启,先上代码

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

@MainThread                                                                        
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;                                                                   
}                                                                                                                                                              

这两个方法都在UI线程调用,其中不指定执行线程池时,默认使用AsyncTask内部实现的SerialExecutor线程池。

这里可以看到对任务的多次执行以及完成的任务再次执行都会抛出异常。

任务开启后调用了onPreExecute(),接着执行构造方法里面初始化的mFuture,上面我们分析过会调用doInBackground(),同时会根据不同状态处理结果,这样任务的开始后的逻辑就可以撸顺了。

任务执行静态方法

上面提到了2个任务执行的实例对象方法,AsyncTask中还存在一个静态方法execute(Runnable runnable)

 @MainThread                                     
 public static void execute(Runnable runnable) { 
     sDefaultExecutor.execute(runnable);         
 }                                               

其实就是用默认实现的SerialExecutor线程池去执行任务,这里可以提交多个任务,上面我们也分析过
SerialExecutor任务是串行执行的,这意味着,这个方法的多个任务也是串行执行的。

验证

  • 先验证,多个实例对象的方法是串行执行的

    public class AsyncTaskTest extends BaseActivity {                             
                                                                                  
        @BindView(R.id.progress_tv)                                               
        TextView mProgressView;                                                   
        @BindView(R.id.content_tv)                                                
        TextView mContentView;                                                    
                                                                                  
        @Override                                                                 
        protected int getContentViewResId() {                                     
            return R.layout.activity_async_task_test;                             
        }                                                                         
                                                                                  
                                                                                  
        @Override                                                                 
        protected void init() {                                                   
            super.init();                                                         
                                                                                  
            try {                                                                 
                for (int i = 0; i < 10; i++) {                                    
                    new AsyncTaskImpl().execute();                                
                }                                                                 
            } catch (Exception e) {                                               
                e.printStackTrace();                                              
            }                                                                     
        }                                                                         
                                                                                  
        private static class AsyncTaskImpl extends AsyncTask {  
                                                                                  
            @Override                                                             
            protected Void doInBackground(Void... voids) {                        
                try {                                                             
                    Thread.sleep(2000);                                           
                    System.out.println(System.currentTimeMillis());               
                } catch (InterruptedException e) {                                
                    e.printStackTrace();                                          
                }                                                                 
                return null;                                                      
            }                                                                     
        }                                                                         
    }                                                                             
                                                                                  
    

    打印如下:

    I/System.out: 1526473028045
    I/System.out: 1526473030050
    I/System.out: 1526473032054
    I/System.out: 1526473034059
    I/System.out: 1526473036061
    I/System.out: 1526473038065
    I/System.out: 1526473040067
    I/System.out: 1526473042070
    I/System.out: 1526473044073
    I/System.out: 1526473046075
    

    每次打印都间隔了2秒以上,符合我们任务串行执行的预期。

  • 再验证一下,调用静态任务执行方法也是串行执行的

        public class AsyncTaskTest extends BaseActivity {                                                                          
                                                                                 
        @Override                                                                
        protected int getContentViewResId() {                                    
            return R.layout.activity_async_task_test;                            
        }                                                                        
                                                                                                                                                    
        @Override                                                                
        protected void init() {                                                  
            super.init();                                                        
                                                                                 
            try {                                                                
                for (int i = 0; i < 10; i++) {                                   
                    AsyncTask.execute(new Runnable() {                           
                        @Override                                                
                        public void run() {                                      
                            try {                                                
                                Thread.sleep(2000);                              
                                System.out.println(System.currentTimeMillis());  
                            } catch (InterruptedException e) {                   
                                e.printStackTrace();                             
                            }                                                    
                        }                                                        
                    });                                                          
                }                                                                
            } catch (Exception e) {                                              
                e.printStackTrace();                                             
            }                                                                    
        }                                                                                                                                                                                                                                 
    }                                                                            
    
    I/System.out: 1526473373076
    I/System.out: 1526473375081
    I/System.out: 1526473377085
    I/System.out: 1526473379090
    I/System.out: 1526473381093
    I/System.out: 1526473383094
    I/System.out: 1526473385096
    I/System.out: 1526473387098
    I/System.out: 1526473389099
    I/System.out: 1526473391101
    

    打印如上,无需多言。

总结

  1. AsyncTask是Android官方提供的用于执行低耗时的工具类
  2. AsyncTask内部使用线程池执行任务
  3. AsyncTask实现多任务串行执行的模式
  4. AsyncTask能将任务执行的各个状态在方法中体现出来
  5. AsyncTask可以被取消,取消方式与FutureTask一致
  6. AsyncTask可以指定任务执行的线程池
  7. 多个AsyncTask的实例对象,默认是串行执行的
  8. 调用静态方法执行多个Runnable也是串行的

你可能感兴趣的:(Android-AsyncTask讲解)