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,當源碼觸發了系統限制或錯誤時,此時我們要反思可能不是系統的問題,而是我們的代碼或實現機制問題。如何解決需謹慎思考,不可盲從高手的解決辦法,不是每個都能控制住這種源碼級修改的引發的其它問題。