1,问题描述:
开发的一项地图应用中,要加载很多层的数据并展示出来,一般地图都是瓦片的,那么不断的滑动,随着地图的可见瓦片不同,需要将这些层的数据不断的加载并显示出来。此时我们使用了异步加载AsyncTask,但滑动了几次或十几次时,会出现“程序异常终止”,此时观察后台日志,则报RejectedExecutionException。
我们使用两层异步任务来实现的,核心的代码如下:
private void loadLayers() {
new LoadLayerAsyncTask(curShowLayers).execute(partExtentMinX, partExtentMinY, partExtentMaxX, partExtentMaxY);
}
//LoadLayerAsyncTask类的核心代码如下:
public class LoadLayerAsyncTask extends AsyncTask<Double, Integer, ArrayList<SubjectOverlay>> { @Override protected ArrayList<SubjectOverlay> doInBackground(Double... params) { ArrayList<SubjectOverlay> layerList= new ArrayList<SubjectOverlay>(); if(showLayers!=null&&showLayers.size()>0){ // 读取部件数据文件 for (LayerInfoBO layerInfo : showLayers) { LoadLayerInfoAsyncTask tempTask = new LoadLayerInfoAsyncTask(layerInfo); tempTask.execute(params[0], params[1], params[2], params[3]); } } return layerList; } }
2,问题分析:
通过对这个异常搜索分析可知,是由于aysncTask线程池的数量限制为128个,当启动的asynctask的个数超过这个时,则会引发线程池的rejectException.网络上有很多牛人,采用了修改android源码,修改限制数量或者调整线程池拒绝策略,来达到修复这个问题。我没有读过android源码,但这个问题真的一定要修改android源码吗?
android设备一般不会超过4个核心,128个线程数量相对较充裕,系统这么限制也较合理,那么一定是我们敲代码的姿势不对了。是的,你仔细看看上面的代码,确实属于姿势不对了。
3,问题原因:
想象我们的操作场景:手指滑动地图,那么应当触发上面个的loadLayers() 方法,这个方法会启动一个一级后台线程LoadLayerAsyncTask。在这个线程的doInBackground方法中,我们可以看到它实际是启用了n个二级线程LoadLayerInfoAsyncTask。也就是说我们滑动一次会启动n个线程,滑动10次会启动10*n个线程,当后台线程池中未执行完的线程数大于128,触发系统限制是必然的了。
4,问题解决:
解决这个问题的思路,有多种方法。此处列出2种,这两种方法均隐含要求每次启动的二级线程数<128:
a, 在一级线程启动二级线程前,我们先取消之前未执行完的二级线程,再启动二级线程。
b, 在启动一级线程时,我们采用提示框机制,二级线程未执行完,则提示框不消失从而禁止用户继续滑动操作触发启动新的一级线程。
5,问题总结:
1,初级的android开发者需谨慎使用这种二级线程机制。一级asynctask一般有提示框机制阻止用户连续操作,隐性使线程数不会达到128个,故使用一级asynctask是较安全的。
2,当源码触发了系统限制或错误时,此时我们要反思可能不是系统的问题,而是我们的代码或实现机制问题。如何解决需谨慎思考,不可盲从高手的解决办法,不是每个都能控制住这种源码级修改的引发的其它问题。