如何诊断ConcurrentModificationException

时间:2016-05-23 17:40:32

标签: android android-volley concurrentmodification

我在崩溃报告中收到以下错误,我在确定它来自何处或如何解决时遇到了一些麻烦。

我感到困惑的原因 - 崩溃似乎引用了Volley库它自己,而不是我实际编写的任何代码(崩溃中的任何内容似乎都指向任何我的代码)。

有谁知道如何或者我可以做些什么来解决这次崩溃?

非常感谢任何建议。

致命错误:

Fatal Exception: java.util.ConcurrentModificationException
       at java.util.HashMap$HashIterator.nextEntry(HashMap.java:787)
       at java.util.HashMap$ValueIterator.next(HashMap.java:819)
       at com.android.volley.toolbox.ImageLoader$4.run(ImageLoader.java:464)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:148)
       at android.app.ActivityThread.main(ActivityThread.java:5527)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:730)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:620)

来源:

public class VolleyManager {
    private static final String TAG = VolleyManager.class.getSimpleName();

    /** Number of network request dispatcher threads to start. */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    private static VolleyManager mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mContext;

    private VolleyManager(Context context) {
        mContext = context.getApplicationContext();
        mRequestQueue = getRequestQueue();
    }

    public static synchronized VolleyManager getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new VolleyManager(context);
        }
        return mInstance;
    }

    @SuppressWarnings("deprecation")
    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            File cacheDir = new File(FMCacheManager.getCacheBaseDir(mContext));

            String userAgent = "volley/0";
            try {
                String packageName = mContext.getPackageName();
                PackageInfo info = mContext.getPackageManager().getPackageInfo(packageName, 0);
                userAgent = packageName + "/" + info.versionCode;
            } catch (NameNotFoundException ignore) {
            }

            HttpStack stack = new HurlStack();
            Network network = new BasicNetwork(stack);

            HandlerThread mHandlerThread = new HandlerThread(TAG, android.os.Process.THREAD_PRIORITY_BACKGROUND);
            mHandlerThread.start();

            ResponseDelivery delivery = new ExecutorDelivery(new Handler(mHandlerThread.getLooper()));
            RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network, DEFAULT_NETWORK_THREAD_POOL_SIZE, delivery);

            queue.start();

            mRequestQueue = queue;
        }
        return mRequestQueue;
    }


    public ImageLoader getImageLoader() {
        getRequestQueue();
        if (mImageLoader == null) {
            mImageLoader = new ImageLoader(this.mRequestQueue,
                    new LruBitmapCache());
        }
        return this.mImageLoader;
    }

    public <T> void addToRequestQueue(Request<T> req, String tag) {
        if (req != null) {
            Log.d(TAG, "request: " + req.getUrl());

            // set the default tag if tag is empty
            req.setTag(FMUtils.isNullOrEmpty(tag) ? TAG : tag);

            getRequestQueue().add(req);
        }
    }

    public void cancelPendingRequests(Object tag) {
        if (mRequestQueue != null) {
            mRequestQueue.cancelAll(tag);
        }
    }

}

1 个答案:

答案 0 :(得分:0)

是的,堆栈跟踪表明崩溃发生在ImageLoader的{​​{1}}方法中,并且与迭代run()有关。

您可以看到(某些版本)HashMap in here。每个循环都有这个:

ImageLoader.java

现在,如果另一个线程在迭代时修改for (BatchedImageRequest bir : mBatchedResponses.values()) { for (ImageContainer container : bir.mContainers) { // If one of the callers in the batched request canceled the request // after the response was received but before it was delivered, // skip them. if (container.mListener == null) { continue; } if (bir.getError() == null) { container.mBitmap = bir.mResponseBitmap; container.mListener.onResponse(container, false); } else { container.mListener.onErrorResponse(bir.getError()); } } } mBatchedResponses.values(),则会发生bir.mContainers。它们不是“线程安全”的集合。

this other recent discussion指出单独的ConcurrentModificationException个实例应该用于任何同步(并行)图像加载操作。 Fernando Jascovich写道:

  

你必须实现两个ImageLoader,一个用于缩略图和   另一个用于全分辨率图像。

因此,您可以检查是否在自己的代码中启动任何并行图像下载操作,查看其他讨论并遵循建议的方法。

关于“如何诊断ConcurrentModificationException”的更通用的答案:请参阅堆栈跟踪,查看确切的代码文件名和行号。如果它在for-each循环中迭代一个集合,你可能同时在另一个线程中修改集合。这可能只是偶尔发生,并且很长时间没有被注意到。当修改所述for-each循环内的集合时也会发生相同的异常,如例如所解释的那样。 in this discussion