多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] ## 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"); ``` 期望是任务线程并行的,如下: ![](https://img.kancloud.cn/b4/12/b412887e97a16a82e734255b369c17b7_495x428.png) 而实际情况是所有的线程都是串行的: ![](https://img.kancloud.cn/c8/77/c87764d1cd0a4717d8572c6fc10579b1_392x483.png) 异步任务是串行的,那么能不能并行呢?查阅资料发现是可以的。 在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<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<Runnable>(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存在的问题和缺陷](https://blog.csdn.net/haovin/article/details/90343595)