深入解析AsyncTask

Android 1.5提供了一个工具类:AsyncTask,它使创建需要与用户界面交互的长时间运行的任务变得更简单。相对来说AsyncTask更轻量级一些,适用于简单的异步处理,不需要借助线程和Handler即可实现。 AsyncTask的功能在不在这里介绍。

 

3.0之前一直以来都是以下面的方式执行AsyncTask任务:

new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                // 处理耗时操作
                return null;
            }
        }.execute();

 

3.0以上的手机上执行AsyncTask都不会使用上面的方式执行了,3.0以上的AsyncTask默认是单线程执行了。

所以要适配不同版本的手机,应该使用下面的工具类执行AsyncTask任务:

 

public class CommonUtils {
    public static <Params, Progress, Result> void executeAsyncTask(
            AsyncTask<Params, Progress, Result> task, Params... params) {
        if (Build.VERSION.SDK_INT >= 11) {
            task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
        } else {
            task.execute(params);
        }
    }
}

 

用法示例:

 

CommonUtils.executeAsyncTask(new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                // 处理耗时操作
                return null;
            }
        });

 

 

 在3.0之后 执行execute默认会调用sDefaultExecutor

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

 

sDefaultExecutor是一功能类似单线程池。THREAD_POOL_EXECUTOR就是和之前一样的工作线程池

 

  public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;

    private static final InternalHandler sHandler = new InternalHandler();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

 

下面看看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);  
        }  
    }  
} 

可以看到,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模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。

不过你可能还不知道,在Android 3.0之前是并没有SerialExecutor这个类的,那个时候是直接在AsyncTask中构建了一个sExecutor常量,并对线程池总大小,同一时刻能够运行的线程数做了规定,代码如下所示:

 

而3.0之后的AsyncTask默认同时只能有1个任务在执行。3.0为了增加不同的需求,增强扩展性,变得更加灵活。如果不想使用默认的线程池,还可以自由地进行配置。比如使用如下的代码来启动任务:

 

Executor exec = new ThreadPoolExecutor(15, 200, 10,  
        TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());  
new DownloadTask().executeOnExecutor(exec);  

这样就可以使用我们自定义的一个Executor来执行任务,而不是使用SerialExecutor。上述代码的效果允许在同一时刻有15个任务正在执行,并且最多能够存储200个任务。

好了,到这里我们就已经把关于AsyncTask的所有重要内容深入浅出地理解了一遍,相信在将来使用它的时候能够更加得心应手。

 

 

 

4.3系统

 

4.4系统的 

   CORE_POOL_SIZE变成了CPU个数+1。

   MAXIMUM_POOL_SIZE变成了2倍的CPU+1

原来的sPoolWorkQueue1在4.4之前队列都是大小为10,4.4之后队列大小是128

 

 

posted on 2014-07-11 14:12  mingfeng002  阅读(229)  评论(0编辑  收藏  举报