AsyncTask线程池异常RejectedExecutionException的解决

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,当源码触发了系统限制或错误时,此时我们要反思可能不是系统的问题,而是我们的代码或实现机制问题。如何解决需谨慎思考,不可盲从高手的解决办法,不是每个都能控制住这种源码级修改的引发的其它问题。

posted @ 2014-12-25 09:50  weiwelcome0  阅读(8520)  评论(0编辑  收藏  举报