Cómo utilizar ListView, ArrayList, ArrayAdapter y notifyDataSetChanged junto con un runnable, y un Handler de mensaje en el hilo principal

Trabajé a través de 6 otros temas de la pregunta tan más algunos postes del blog con el mesage del error mismo, todo en ninguna utilidad.

Estoy cargando un directorio de archivos en un ArrayList y luego en un ListView usando un ArrayAdapter . El código funciona realmente – el directorio muestra y responde a los eventos de clic – pero el programa termina con este error, generalmente después de mostrar el directorio uno, algunas o varias veces y luego ejecutar otra parte del programa que hace un loadUrl en un WebView:

El contenido del adaptador ha cambiado pero ListView no recibió una notificación. Asegúrese de que el contenido de su adaptador no se ha modificado desde un subproceso de fondo, sino sólo desde el subproceso de interfaz de usuario. Asegúrese de que su adaptador llama a notifyDataSetChanged () cuando cambia su contenido.

Ninguno de los ejemplos simples que he visto utilizan notifyDataSetChanged o un hilo separado. Me pregunto si necesito usarlos y cómo usarlos correctamente. Estoy confundido entre el ArrayAdapter y el ArrayList respecto a lo que necesita ser modificado sólo en el hilo principal. Los datos en el ArrayList se cambian sólo cuando se solicita una nueva lista de directorio, luego se borra y se carga con los nuevos datos.

Mis preguntas son: ¿necesito el notifyDataSetChanged en este caso? [Estoy seguro de que no lo hago], y ¿cómo puedo organizar las diversas partes entre el principal y el fondo / runnable hilo?

Hago que el programa se bloquee ejecutando alternativamente las secciones directoryList y displayFtpHelp [usa WebView / loadUrl ()] – pero debo ejecutar el par varias o muchas veces para obtener el bloqueo.

El LogCat no incluye la última instrucción ejecutada en el programa, por lo que es difícil identificar el disparador real; Más bien es esto:

 android.widget.ListView.layoutChildren(ListView.java:1555) 

La sección displayFtpHelp loadUrl () en un WebView. Me pregunto cómo esto eventualmente desencadena el error. Parece que el programa realmente no está funcionando en ArrayList / ArrayAdapter cuando se produce el error. Parece un error de estructura de programa en lugar de un error técnico o lógico.

Lo que estoy haciendo es, primero definir dos ListViews y el ArrayList. Un ListView es para los archivos de dispositivos locales, el otro para un sitio FTP remoto.

 static ListView filesListLocal, filesListRemote; static ArrayList<HashMap<String,String>> directoryEntries = new ArrayList<>(); 

Mostraré código para el directorio Ftp. El código para el directorio local es esencialmente el mismo, excepto por el uso de file.getAbsolutePath() lugar de ftp.list() , y los cambios técnicos a lookForFilesAndDirs (), que es recursivo para el procesamiento del directorio local.

Entonces leí el directorio de Ftp en una matriz usando un Runnable. Estoy omitiendo la comprobación de errores y otros detalles aquí; Esto está utilizando la biblioteca Ftp4j:

 Runnable r = new Runnable() { public void run() { Message msg = mHandler.obtainMessage(); try { FTPFile[] fileAndDirs = ftp.list(); dirName = ftp.currentDirectory(); } catch (Various Exceptions e) { result = "The operation failed\n" + e.toString(); } Bundle bundle = new Bundle(); bundle.putString("mickKey", result); msg.setData(bundle); mHandler.sendMessage(msg); } }; Thread t = new Thread(r); t.start(); 

En el controlador de mensajes i copiar los datos de la matriz a la ArrayList y utilizar el adaptador para mostrarlo en el ListView. Originalmente tenía este código en el Runnable, luego se trasladó al manejador de mensajes en el hilo principal cuando recibí el mensaje de error, pero que no ayudó. Puse en las llamadas notifyDataSetChanged en una conjetura pero también hizo ninguna diferencia:

 dirName = ftp.currentDirectory(); sa.notifyDataSetChanged(); directoryEntries.clear(); lookForFilesAndDirs(fileAndDirs); // load ArrayList SimpleAdapter saFtp = new SimpleAdapter(myContext, directoryEntries, R.layout.my_two_lines, new String[] {"path", "filename"}, new int[] {R.id.path, R.id.filename}); filesListRemote.setAdapter(saFtp); sa.notifyDataSetChanged(); 

… dejando fuera más detalles aquí

 public static void lookForFilesAndDirs(FTPFile[] fileAndDirs) { for (FTPFile fileOrDir : fileAndDirs) { String fileOrDirName = fileOrDir.getName(); int entryType = fileOrDir.getType(); if (entryType == 1) { // entry is a directory HashMap<String,String> listEntry = new HashMap<>(); listEntry.put("path", fileOrDirName); listEntry.put("filename", null); directoryEntries.add(listEntry); } else { // entry is a file HashMap<String,String> listEntry = new HashMap<>(); listEntry.put("path", dirName); listEntry.put("filename", fileOrDirName); directoryEntries.add(listEntry); } } 

El LogCat:

 10-14 19:47:29.403 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: onMenuItemClick 10-14 19:47:29.403 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: ftpPrintFilesList 10-14 19:47:30.235 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: handleMessage 10-14 19:47:32.339 3006-3006/com.webs.mdawdy.htmlspyii W/EGL_genymotion: eglSurfaceAttrib not implemented 10-14 19:47:34.627 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: onMenuItemClick 10-14 19:47:34.627 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: displayFtpHelp Begin 10-14 19:47:34.627 3006-3006/com.webs.mdawdy.htmlspyii I/Mick: displayFtpHelp end 10-14 19:47:34.819 3006-3006/com.webs.mdawdy.htmlspyii D/AndroidRuntime: Shutting down VM 10-14 19:47:34.819 3006-3006/com.webs.mdawdy.htmlspyii W/dalvikvm: threadid=1: thread exiting with uncaught exception (group=0xa4d2db20) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: FATAL EXCEPTION: main 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: Process: com.webs.mdawdy.htmlspyii, PID: 3006 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(2131427419, class android.widget.ListView) with Adapter(class android.widget.SimpleAdapter)] 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.widget.ListView.layoutChildren(ListView.java:1555) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.widget.ListView.setSelectionInt(ListView.java:1980) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.widget.AbsListView.resurrectSelection(AbsListView.java:5376) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.widget.AbsListView.onWindowFocusChanged(AbsListView.java:2822) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.view.View.dispatchWindowFocusChanged(View.java:7900) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:968) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.view.ViewGroup.dispatchWindowFocusChanged(ViewGroup.java:972) [6 more lines identical to the one above] 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.view.ViewRootImpl$ViewRootHandler.handleMessage(ViewRootImpl.java:3133) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:102) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.os.Looper.loop(Looper.java:136) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:5001) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at java.lang.reflect.Method.invokeNative(Native Method) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at java.lang.reflect.Method.invoke(Method.java:515) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601) 10-14 19:47:34.823 3006-3006/com.webs.mdawdy.htmlspyii E/AndroidRuntime: at dalvik.system.NativeStart.main(Native Method) 

One Solution collect form web for “Cómo utilizar ListView, ArrayList, ArrayAdapter y notifyDataSetChanged junto con un runnable, y un Handler de mensaje en el hilo principal”

Algunas sugerencias:

  1. Utilice listas de entrada de directorio independientes para almacenar los datos que respaldan los dos ListView .

    • List<HashMap<String,String>> directoryEntriesLocal = new ArrayList<>();
    • List<HashMap<String,String>> directoryEntriesRemote = new ArrayList<>();
  2. Establezca los adaptadores sa y saFtp a las saFtp de lista respectivas en el método onCreate() de la actividad. En esta etapa, tanto directoryEntriesLocal como directoryEntriesRemote pueden estar vacíos.

  3. En el Handler para el hilo principal, basta con modificar los datos de la lista de entrada de directorio que necesita actualizarse y luego llamar a notifyDataSetChanged() del adaptador correspondiente . Será mejor si declaró dos Handlers, uno para actualizar la lista remota y el otro para la lista local. O usted puede hacer ambos en un solo Handler . Esa decisión le queda a usted. No es necesario instanciar y asignar nuevos adaptadores aquí.
  4. Por favor, elimine el uso directo de las clases Handler y Thread . En su lugar, utilice AsyncTask . Esto hará que su vida como un programador Android mucho más fácil.
  • Cómo evitar la renovación de las celdas al llamar a notifyDataSetChanged () para PinterestLikeAdapterView?
  • Hace notificydatasetchanged llamada onCreateViewHolder al usar RecyclerView
  • Android ParseQueryAdapter notifyDataSetChanged no funciona
  • Cómo actualizar algunos datos en un ListView sin utilizar notifyDataSetChanged ()?
  • NotifyDataSetChange no funciona desde un adaptador personalizado
  • ¿Por qué no "notifyDatasetChanged ()" notifica todos los elementos visibles?
  • ¿Por qué no necesito usar Adapter.notifyDataSetChanged ()?
  • El adaptador de RecyclerView notifyDataSetChanged no funciona
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.