【问题标题】:Android - setVisibility results in java.util.ConcurrentModificationExceptionAndroid - setVisibility 导致 java.util.ConcurrentModificationException
【发布时间】:2012-03-27 23:58:53
【问题描述】:

我正在通过setVisibility(View.INVISIBLE) 隐藏视图。稍后当我尝试通过 setVisibility(View.VISIBLE) 以不同的方法再次显示视图时,我得到以下异常

03-28 01:32:05.450: E/AndroidRuntime(20895): FATAL EXCEPTION: main
03-28 01:32:05.450: E/AndroidRuntime(20895): java.util.ConcurrentModificationException
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.util.HashMap$HashIterator.nextEntry(HashMap.java:796)
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.util.HashMap$KeyIterator.next(HashMap.java:823)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:946)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewGroup.dispatchDragEvent(ViewGroup.java:948)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewRoot.handleDragEvent(ViewRoot.java:3027)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.view.ViewRoot.handleMessage(ViewRoot.java:2185)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.os.Handler.dispatchMessage(Handler.java:99)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.os.Looper.loop(Looper.java:132)
03-28 01:32:05.450: E/AndroidRuntime(20895): at android.app.ActivityThread.main(ActivityThread.java:4028)
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.lang.reflect.Method.invokeNative(Native Method)
03-28 01:32:05.450: E/AndroidRuntime(20895): at java.lang.reflect.Method.invoke(Method.java:491) 
03-28 01:32:05.450: E/AndroidRuntime(20895): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
03-28 01:32:05.450: E/AndroidRuntime(20895): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
03-28 01:32:05.450: E/AndroidRuntime(20895): at dalvik.system.NativeStart.main(Native Method)

当我注释掉将可见性更改回可见的行时,我没有得到异常。

我首先认为异常可能是由一些其他代码迭代哈希图引起的,但是,我在迭代我使用的哈希图时没有做任何修改,我也没有多线程,这似乎是最此异常的常见原因。当我不改变可见性时,我也没有得到例外。

编辑
异常发生在自定义片段中。下面是我迭代哈希图 (mWidgetConfig) 的代码,其中包含有关我要恢复的自定义小部件的配置的信息。 hashmap 是片段中的公共变量。

在一个由fragment创建的OnDragListener中,我根据一定的拖拽操作更新hashmap,如下:

// Update the widget configuration of the fragment that created this listener
                mFragment.mWidgetConfig.put(startCircleTag, "0");

我还遍历哈希图以检查某个条件,但在迭代期间我没有做任何修改:

Iterator<String> keySetItr = mFragment.mWidgetConfig.keySet().iterator();
        while(keySetItr.hasNext()) {
            String tag = keySetItr.next();
            if(mFragment.mWidgetConfig.get(tag).equals((String) destSocket.getTag())) {
                // do something, though no modification of the hashmap
                break;

            }
        }

此外,我在尝试恢复小部件配置时对片段本身进行了一次迭代。下面是我用来根据 hashmap 配置小部件的代码:

    public void configureWidgets() {
    resetWidgets();

    Iterator<String> keySetItr = mWidgetConfig.keySet().iterator();
    while(keySetItr.hasNext()) {
        String tag = keySetItr.next();
        Integer value = Integer.parseInt(mWidgetConfig.get(tag));

        ImageView destSocket = null;
        switch(value) {
        case 0:
            // The circle will not be connected to any socket
            continue;
        case 1:
            destSocket = mSocket1;
            break;
        case 2:
            destSocket = mSocket2;
            break;
        case 3:
            destSocket = mSocket3;
            break;
        }

        ImageView startCircle = (ImageView) mLayout.findViewWithTag(tag);
        ImageView startPlug = (ImageView) mLayout.findViewWithTag(tag + "_plug");

        // Replace the drawable of destSocket
        destSocket.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket_plugged));

        // Hide plug view
        startPlug.setVisibility(View.INVISIBLE);

        // Draw a line between the start circle view and the destination socket view
        mConnectionLinesView.addLine(startCircle, destSocket);
    }
}


public void resetWidgets() {
    // Remove all lines
    mConnectionLinesView.removeLines();

    // Show all eventually previously hidden plugs
    //mPlug1.setVisibility(View.VISIBLE);
    //mPlug2.setVisibility(View.VISIBLE);
    //mPlug3.setVisibility(View.VISIBLE);

    // Set to backround drawable of the socket to the initial one
    mSocket1.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket).mutate());
    mSocket2.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket).mutate());
    mSocket3.setBackgroundDrawable(getActivity().getResources().getDrawable(R.drawable.socket).mutate());
}

只要在代码中使用了设置上述“插头”可见性的行,就会出现异常。

解决方案
抛出异常的原因是我调用了OnDragListenerDragEvent.ACTION_DRAG_ENDED case 语句中的配置方法。当我将相同的代码放入DragEvent.ACTION_DROP case 语句时,不会引发异常。不知道为什么。谢谢你们的帮助

【问题讨论】:

  • 发布您的代码以迭代 hashmap ...并创建此异常
  • 那么为什么只有在我更改可见性时才抛出异常?
  • 是删除或添加while(iterator.hasNext())
  • 不,没有,这很奇怪:(
  • 会有所帮助,但使用 HashMap 的部分是实际的 Android 代码 ;)

标签: android android-layout


【解决方案1】:

据我了解,这是由ViewGroup 实现细节引起的。而且它与多线程无关。

当拖动开始时ViewGroup 创建一个必须通知ACTION_DRAG_ENDED 事件的子视图的HashSet。这是一组可见的孩子。当子级可见性改变时,相应的ViewGroup 会修改该集合(如果其可见性为VISIBLE,则添加一个子级)。在您的情况下,它发生在该集合的迭代期间。

想一想,对您来说最简单的解决方案是推迟可见性更改。

view.post(new Runnable() {
  public void run() {
    view.setVisibility(View.VISIBLE);
  }
});

当您将代码放入 ACTION_DROP case 语句时不会发生异常,因为在您更改视图可见性时该集合并未被迭代。

详情见ViewGroup.dispatchDragEvent(DragEvent)源码。

【讨论】:

    【解决方案2】:

    尝试使用:

    setVisibility(View.GONE);
    setVisibility(View.VISIBLE);
    

    【讨论】:

    • 不幸的是这没有帮助,我仍然得到同样的异常
    • 这似乎不是 setVisibility 部分的问题。这个网址可能会给你一些关于这个问题的提示 - javacodegeeks.com/2011/05/…
    • 就是这样,我在单线程环境中,在迭代过程中我没有做任何修改。如果 Android 以某种方式自动线程化您的应用程序,这可能就是答案
    【解决方案3】:

    另一种可能的解决方案是将您的可拖动视图包裹在一些 ViewGroup(例如 FrameLayout)中并始终保持可见。

    这样,只有其中包含的可拖动视图会消失,在原来的位置留下一个洞(就像以前一样),但包装器的父级不会收到有关可拖动被隐藏的通知。

    基本上是从

    ViewGroup:parent
     ┗ View:draggable (toggling setVisible on this one, parent gets notified)
    

    ViewGroup:parent
     ┗ ViewGroup:wrapper (setVisible never called on this one)
         ┗ View:draggable (toggling setVisible on this one, wrapper gets notified)
    

    这里避免使用ConcurrentModificationException,因为有问题的Map 只包含一个元素,因此进行了一次迭代,这意味着对Iterator.hasNext/.next 的一次调用。

    你自己决定这是否是一个 hack :)

    注意:您的编辑与问题无关,因为例外是关于 ViewGroup.mDragNotifiedChildren 而不是您的 Map(请参阅堆栈跟踪),

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-04
      • 2020-06-16
      • 1970-01-01
      • 1970-01-01
      • 2016-06-11
      • 1970-01-01
      相关资源
      最近更新 更多