解决Android中AsyncTask的多线程阻塞问题

Android开发中执行耗时操作并更新UI时,通常有三种方式:
1.直接调用runOnUiThread(new Runnable(){}),使用简单,但不能在Activity之外的环境使用,如View、Dialog等;
2.使用AsyncTask实现,通过onPreExecute()、doInBackground()、onPostExecute三个方法能方便的分开UI操作和耗时操作,避免UI线程阻塞,并且支持参数传递;
3.Handler结合Message实现,比较重量级,但还需要用到Looper等,而且Message封装的Bundle对象不能太大,否则会抛异常。
几种方式各有利弊,实际开发中应根据需要选取合适方式,如Activity中可采用方法1和3,其他场合可使用方式2。

公司的项目中最近经常出现数据加载不出来的问题,而且切换网络环境后,出现概率会变小,因此起初一直以为是HTTP请求模块的问题,将Socket的timeout设置为一分钟后,一开始确实没有再重现,但过了几天问题又重现了,于是通过转包并请后台API相关开发人员查看日志分析,发现请求根本都没发出去!这样进一步确定是APP这边的问题,但究竟出在哪里一片茫然。
今天进行代码重构,涉及到AsyncTask这部分代码,通过logcat竟然发现onPreExecute()方法执行之后迟迟不进入doInBackground()方法!看来这就是问题所在。前一个task执行后,后一个task一直无法从pending状态进入running状态,说明线程阻塞了。
仍然是从StackOverflow上找到了启示:
API 11以前的AsyncTask线程池大小默认size为1,所以很容易阻塞,而之后的API提供了一个新方法executeOnExecutor(Executor exec, Params… params),第一个参数有两个常量值可选:
1.SERIAL_EXECUTOR: 相当于之前的execute()方法,多个task串行,可能阻塞;
2.THREAD_POOL_EXECUTOR: 线程池,默认size为5;
这样我们可通过判断API Level调用不同的方法:

@TargetApi(Build.VERSION_CODES.HONEYCOMB)
void startMyTask(AsyncTask asyncTask) {
    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
        asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
    }else{
        asyncTask.execute(params);
    }
}

但是如果需要同时执行更多task(当然这个要尽量避免),仅仅使用AsyncTask也搞不定了,还需要通过BlockingQueue和ThreadPoolExecutor定义自己的线程池:

int corePoolSize = 60;
int maximumPoolSize = 80;
int keepAliveTime = 10;
BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<Runnable>(maximumPoolSize);
Executor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS, workQueue);
asyncTask.executeOnExecutor(threadPoolExecutor);

这样同时跑多个task线程就毫无压力了。

原文地址:https://www.cnblogs.com/stevenlxq/p/3600847.html