RecyclerView y DiffUtil – Una pesadilla de concurrencia

La documentación de DiffUtil sugiere generar el DiffUtil.DiffResult en un subproceso de fondo debido a posibles tiempos de cálculo largos. Esto parece una mala idea para mí, porque ese hilo podría estar funcionando con datos obsoletos en una situación como la siguiente (suponiendo que el acceso a la list es seguro de subproceso):

  1. Agregar datos a la list y notificar al adaptador
  2. Necesidad de reemplazar la list con newList que tendría un diff de algunas adiciones y algunas remociones
  3. Llame a DiffUtil.calculateDiff en segundo plano y obtenga el DiffResult para list y newList y publique un mensaje en el subproceso principal que utilizará newList y llamará DiffResult.dispatchUpdatesTo
  4. Antes de que se maneje ese mensaje, el usuario toma una acción en el hilo principal que hace que las mutaciones aparezcan en la list
  5. El mensaje se maneja, newList se establece para ser el nuevo origen de datos y DiffResult.dispatchUpdatesTo se ejecuta causando una vista inconsistente de los datos subyacentes + una pérdida de cualquier mutación desde el DiffResults se calcularon

Bueno, eso no es bueno, así que cambiemos a partir del paso 3:

  1. Establezca newList como el nuevo origen de datos, llame a DiffUtil.calculateDiff en segundo plano y obtenga el DiffResult para list y newList y publique un mensaje en el subproceso principal que llamará DiffResult.dispatchUpdatesTo
  2. Antes de que se maneje ese mensaje, el usuario toma una acción en el subproceso principal que causa las mutaciones a newList , y notifica al adaptador, causando una vista inconsistente de los datos porque DiffResult.dispatchUpdatesTo no ha sido llamado aún

Hay más variaciones en esto, pero ninguno es bueno. Parece que la única manera de utilizar DiffUtil forma fiable con un conjunto de datos grande y un conjunto de cambios es desactivar o poner en cola todas las actualizaciones hasta que se DiffResult.dispatchUpdatesTo .

¿Me estoy perdiendo algo que haría lo anterior falso?

Echa un vistazo a BatchingListUpdateCallback. Es la clase, que se envuelve la devolución de llamada principal y cuando se produjo un cambio múltiple en la lista, el batchingListCallback notificará sólo una vez que la devolución de llamada principal.

https://developer.android.com/reference/android/support/v7/util/BatchingListUpdateCallback.html

EDITAR

Siento que mi respuesta no sea correcta.

Miré en el código fuente DiffUtil. Y veo, que el BatchingListUpdateCallback se utiliza en el método dispatchUpdatesTo todos modos.

  public void dispatchUpdatesTo(ListUpdateCallback updateCallback) { final BatchingListUpdateCallback batchingCallback; if (updateCallback instanceof BatchingListUpdateCallback) { batchingCallback = (BatchingListUpdateCallback) updateCallback; } else { batchingCallback = new BatchingListUpdateCallback(updateCallback); // replace updateCallback with a batching callback and override references to // updateCallback so that we don't call it directly by mistake //noinspection UnusedAssignment updateCallback = batchingCallback; } 

Pero como vector para el camino correcto esto podría ser útil 🙂

Acabé apilando actualizaciones para gestionar la interacción del usuario durante los cálculos DiffUtil y todavía acceder a datos en el hilo principal sólo.

Lo escribí allí: https://geoffreymetais.github.io/code/diffutil-threading/

  • Selección de un valor RadioButton y desplazamiento hacia atrás eliminando el seleccionado en RecyclerView
  • RecyclerView dentro de ScrollView / NestedScrollView no se desplaza correctamente
  • ¿Cómo utilizar ContentObserver con RecyclerView?
  • Error: El hijo especificado ya tiene un padre. Debe llamar a removeView () en el padre del niño primero
  • ScrollBy no funciona correctamente en recyclerview anidado
  • RecyclerView está cortando el último elemento
  • Tools: text para artículos de RecyclerView
  • Uso de CardView y RecyclerView en mis archivos de diseño genera una excepción
  • Android 5.0 (Lollipop) CheckedTextView presione destacar problemas
  • v7 RecyclerView NullPointerException AccessibilityDelegateCompat.getBridge ()
  • Hace notificydatasetchanged llamada onCreateViewHolder al usar RecyclerView
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.