1、串行还是并行
实际后台线程只有一个,即所有的任务是串行的,即完成一个任务后再执行下一个任务,而非并行。
如果开多个任务,比如开三个任务,实际执行是串行的:
mAsyncTask task1 = new mAsyncTask();
mAsyncTask task2 = new mAsyncTask();
mAsyncTask task3 = new mAsyncTask();
task1.execute("1", "2", "3");
task2.execute("a", "b", "c");
task3.execute("x", "y", "z");
期望是任务线程并行的,如下:
而实际情况是所有的线程都是串行的:
异步任务是串行的,那么能不能并行呢?查阅资料发现是可以的。
在1.6之前,AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从3.0开始,为了避免AsyncTask所带来的并发错误,AsyncTask可以根据配置采用串行或并行执行任务,但是默认是采用一个线程来串行执行任务,若想并行执行任务,可以调用 executeOnExecutor (Executor exec, Params... params)方法。
该方法有两个参数,第一个是Executor ,第二个是任务参数,第一个是线程池实例,这里已经预定义了两种,AsyncTask.SERIAL_EXECUTOR和AsyncTask.THREAD_POOL_EXECUTOR,通过源代码可以看到,execute (Params... params)方法实际是调用的就是executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params),即串行执行,如果想并行执行异步任务,可调用executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params)方法。
2、任务线程过多
异步任务并行处理时,AsyncTask最多可以同时执行的任务数量有限,同时队列中可以有128个在等待。 如果超过这个数字,就会报java.util.concurrent.RejectedExecutionException的异常(超出线程池容量以及队列长度后拒绝任务的策略)。
AsyncTask.THREAD_POOL_EXECUTOR线程池的配置如下:
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
threadPoolExecutor.allowCoreThreadTimeOut(true);
THREAD_POOL_EXECUTOR = threadPoolExecutor;
其中:
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));//设置线程池的核心线程数2-4之间,但是取决于CPU核数
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//最多工作线程数量
private static final int KEEP_ALIVE_SECONDS = 30;
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(128);//等待队列,等待128个
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());
}
};
所以AsyncTask可能存在新开大量线程消耗系统资源和抛出异常,如没有捕获RejectedExecutionException异常会导致应用FC的风险。
访问网络频繁或者需要大量线程的程序应慎用AsyncTask。
3、生命周期
AsyncTask不与任何组件绑定生命周期,即在一个Activity中创建AsyncTask,当该Activity被销毁时AsyncTask并没有被同步销毁,而是在一直执行,直到doInBackground()方法执行完成。如果手动调用了cancel()方法,系统会自动调用onCancelled()方法,否则会执行onPostExecute()方法。
所以这里就会产生一个问题,Activity已经被销毁,而AsyncTask还在正常工作,还会对View做一些处理,但是此时的View已经不存在了。
所以应该确保在销毁Activity的同时,取消AsyncTask,即在Activity的onDestory()内调用cancel()方法。
4、任务执行结果丢失问题
当Activity重新创建时(屏幕旋转时或Activity意外销毁时恢复),AsyncTask任务正常运行,AsyncTask持有的销毁之前的Activity引用已经无效,而AsyncTask任务执行完成后,在onPostExecute()方法内修改UI也不会生效。
所以最好的办法是,当恢复Activity时重启AsyncTask任务。
5、内存泄漏
如果AsyncTask被声明为Activity的非静态内部类,那么AsyncTask会默认保留一个对该Activity的引用,如果这个Activity已经被销毁,因这种引用关系的存在,造成该Activity无法被回收,造成内存泄漏。
所以解决办法可以把AsyncTask声明为Activity的静态内部类。