Android – setVisibility resultados en java.util.ConcurrentModificationException

Estoy ocultando una vista a través de setVisibility(View.INVISIBLE) . Más adelante, cuando intento mostrar la vista de nuevo en un método diferente a través de setVisibility(View.VISIBLE) , obtengo la siguiente excepción

 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) 

Cuando comento la línea que cambia la visibilidad de nuevo a visible, no obtengo la excepción.

Pensé primero que la excepción sería causada por algún otro código iterating a través de un hashmap, sin embargo, no hago ninguna modificación mientras que iterating a través de los hashmaps que utilizo, tampoco tengo multithreading, que parece ser la razón más común para Esta excepción. También no consigo la excepción cuando no cambio detrás la visibilidad.

EDIT :
La excepción se produce en un fragmento personalizado. A continuación se muestra el código en el que iterar sobre el hashmap ( mWidgetConfig ) que contiene información acerca de la configuración de widgets personalizados que estoy intentando restaurar. El hashmap es una variable pública en el fragmento.

En un OnDragListener que es creado por el fragmento, actualizar el hashmap de acuerdo con una operación de arrastre determinado, como esto:

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

También iterate sobre el hashmap para comprobar una cierta condición pero no hago ninguna modificación durante la iteración:

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

Además hago una iteración en el fragmento mismo mientras intento restaurar la configuración del widget. A continuación se muestra el código que utilizo para configurar el widget según el 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()); } 

Tan pronto como las líneas que fijan la visibilidad de los "enchufes" arriba se utilizan en el código, consigo la excepción.

SOLUCIÓN
La razón por la excepción se lanzó es que he llamado a los métodos de configuración en la declaración de caso DragEvent.ACTION_DRAG_ENDED de OnDragListener . Cuando pongo el mismo código en la declaración de caso DragEvent.ACTION_DROP la excepción no se lanza. Ninguna pista por qué. Gracias por su ayuda chicos

Según entiendo, esto se debe a los detalles de implementación de ViewGroup . Y no está conectado con multithreading.

Cuando arranca arranca ViewGroup crea un HashSet de vistas secundarias que debe ser notificado sobre el evento ACTION_DRAG_ENDED . Este es un conjunto de niños visibles. Cuando se cambia la visibilidad de un niño, un grupo de ViewGroup correspondiente modifica dicho conjunto (agregando un niño si su visibilidad es VISIBLE ). Y en su caso sucede durante iteraciones sobre esa colección.

Piense, la solución más fácil para usted es posponer el cambio de visibilidad.

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

Y la excepción no sucede cuando pones tu código en la ACTION_DROP caso porque ese conjunto no está siendo iterado en el momento que cambias la visibilidad de la vista.

Para obtener más información, consulte el código fuente ViewGroup.dispatchDragEvent (DragEvent) .

Trate de usar:

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

Otra posible solución es envolver su vista arrastrable en algún ViewGroup (por ejemplo, FrameLayout ) y mantenerlo visible todo el tiempo.

De esta manera sólo desaparecerá la vista arrastrada contenida dentro de ella, dejando un agujero donde estaba (igual que antes), pero el padre del envoltorio no recibirá notificación de que el objeto arrastrable está oculto.

Esencialmente pasar de

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

a

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

La ConcurrentModificationException se evita aquí porque el Map problemático contiene solamente un elemento, por lo tanto una iteración se hace, que significa una llamada a .next / .next .

Decide por ti mismo si se trata de un hack 🙂

Nota: su edición no tiene nada que ver con la pregunta, ya que la excepción se trata de ViewGroup.mDragNotifiedChildren y no de su Map (consulte stacktrace),

  • Cómo agregar espacio entre los botones de pestaña en Android?
  • Incluir el mismo diseño varias veces
  • ¿Cómo desplazar un diseño desplazable en el Android Studio?
  • TextInputLayout no se muestra cuando se agrega programativamente
  • Vista previa de enlaces en android
  • Android busca recursos en la carpeta de retrato incluso cuando la aplicación está bloqueada en Paisaje
  • Falta de ActionBar en el diseño del material
  • Cómo crear un formulario de entrada de dos columnas en Android?
  • Android studio AssertionError: No invocar waitForSmartMode desde dentro de la acción de lectura en modo mudo
  • ViewPager en CoordinatorLayout se contrae inesperadamente
  • LinearLayout no llena tablerow incluso la anchura fill_parent se llama
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.