Android -设置可见性导致java.util.并发修改异常

htrmnn0y  于 2023-01-03  发布在  Android
关注(0)|答案(3)|浏览(140)

我通过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)

当我注解掉将可见性更改回visible的行时,我没有得到异常。
我最初认为这个异常可能是由其他代码迭代哈希Map引起的,然而,我在迭代哈希Map时没有做任何修改,我也没有多线程,这似乎是这个异常最常见的原因。而且,当我不改变可见性时,我也不会得到这个异常。

    • 编辑**:

这个异常发生在一个自定义片段中。下面是我迭代hashmap(mWidgetConfig)的代码,其中包含了我试图恢复的自定义小部件的配置信息。hashmap是片段中的一个公共变量。
在由片段创建的OnDragListener中,我根据某个拖动操作更新散列表,如下所示:

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

我还迭代哈希Map来检查某个条件,但在迭代过程中不做任何修改:

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;

            }
        }

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

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());
}

一旦代码中使用了设置上述"plugs"可见性的行,我就得到了异常。

    • 解决方案**

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

oewdyzsn

oewdyzsn1#

据我所知,这是由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)源代码。

u4dcyp6a

u4dcyp6a2#

尝试使用:

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

lymnna713#

另一种可能的解决方案是将可拖动视图 Package 在某个ViewGroup中(例如FrameLayout),并始终保持其可见。
这样,只有包含在其中的可拖动视图会消失,在原来的位置留下一个洞(就像以前一样),但是 Package 器的父级不会得到可拖动视图被隐藏的通知。
基本上从

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的一次调用。

  • 自己决定这是否是一个黑客:)*

注意:您的编辑与问题无关,因为例外是关于ViewGroup.mDragNotifiedChildren的,而不是您的Map(参见stacktrace),

相关问题