Android: la mejor práctica para realizar operaciones asíncronas en getView ()

Por favor, no cierre esto, IMHO es cuestión de programación decente y posiblemente útil.


Por favor estoy leyendo un montón de cosas, y me estoy confundiendo porque leo diferentes opiniones y enfoques diferentes.

El problema es el siguiente:

En el getView() de un Adapter Tengo que realizar alguna operación asíncrona, como comprobar una formación en la web, y actualizar la vista basada en eso.

He utilizado el siguiente enfoque:

Cada vez que getView() se llama getView() un Thread

Pero mi enfoque como me ganó un montón de críticas:

Https://stackoverflow.com/a/28484345/1815311

Https://stackoverflow.com/a/28484335/1815311

Https://stackoverflow.com/a/28484351/1815311

 public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { //... } else { //... } Thread th= new Thread(new Runnable() { @Override public void run() { mActivity.runOnUiThread(new Runnable() { @Override public void run() { CheckSomeInfoOverTheInternet(url, new myCallback { @Override public void onSuccess() { holder.textview.setText("OK"); } @Override public void onFailre() { holder.textview.setText("NOT OK!!!!"); } }); } }); } }); th.start(); return convertView; } 

Por favor, ¿cuál sería la mejor práctica para hacer tal cosa?


Tenga en cuenta que no busco una solución para ejecutar las solicitudes de red en getView() , sino cómo actualizar la vista en función del resultado de la llamada asíncrona.

Hay varios appraoches para esto. Aunque lo que estás haciendo realmente no es apropiado.

  1. AsyncTask

    • El hilo de agrupación aquí se hace internamente, por lo que no es necesario que se moleste con eso
    • Es un enfoque más limpio de su problema en lugar de generar hilos individuales.
    • Si el usuario cambia la pantalla durante la llamada a la API, también puede cancelar la llamada .
    • Debería habilitar notifyDatasetChanged ()
    • Necesita anular muy pocas funciones para lograr la funcionalidad que desea.
  2. AsyncTaskLoader

    • Te da más control pero pierdes en varias funciones implícitamente definidas
    • Necesita más conocimientos para usar esto y debe estar bien versado con clases como LoaderManager , Loader .
    • El cambio es auto trigerring Diga si usted cambiara su conjunto de datos subyacente, los cambios se dispararían automáticamente y proporcionarían un cambio a su interfaz de usuario.
  3. Manipuladores e hilos

    • Este es un stpe por encima de su actual appraoch pero proporcionan manera más benifits
    • Puede abstraer la creación del hilo y proporcionar un controlador que manejaría la llamada para todos sus ids.
    • Puede poner en cola los subprocesos y los mensajes entregados.
    • Si cambia la pantalla, puede eliminar las devoluciones de llamada y los mensajes .

En conclusión, los principales inconvenientes de su enfoque actual: – es la pérdida de contexto cuando los cambios deben hacerse. – la creación explícita de múltiples hilos

Si bien este último es un problema importante, el problema más "sensible al usuario" sería el primero.

Hay y podría ser varios otros enfoques basados ​​en el control que requiere y su experiencia con devoluciones android and thread management.But estos tres son (según mí) más adecuado.

PS : el punto común en todos estos enfoques es,

 public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { //... } else { //... } //execute a task for your given id where task could be: //1. AsyncTask //2. AsyncTaskLoader //3. Handlers and thread //call notifyDataSetChanged() in all the cases, return convertView; } @Override public void notifyDataSetChanged() { super.notifyDataSetChanged(); //do any tasks which you feel are required } 

PPS : También puede buscar en DataSetObserver para automatizar de nuevo sus requisitos.

Esto no es absolutamente una buena manera de actualizar la información en un ListView . El método getView debe simplemente crear la vista a partir de datos que ya tiene. Ciertamente, no debería estar ejecutando nada para obtener más información.

El mejor consejo que podría darte es buscar los datos de antemano. Tire de los datos, actualice el ArrayList que su Adapter está conectado a, a continuación, llamar a adapter.notifyDataSetChanged() . Esto rediseñará toda su información.

Tire de los datos de una vez – no en pequeñas partes. Esta es la mejor y más razonable manera de hacer esto.

EDITAR

Creo que esta es una pregunta interesante, vale la pena algún tipo de solución "canónica"

Google I / O 2013 : PI te sugiere que mires este Google I / O a partir de 2013. Ellos han explicado claramente muchas de estas cosas allí. Todas sus preguntas serán contestadas allí. Es un canon.

He utilizado la biblioteca de Volley aquí. Si usted lee los documentos entonces usted verá que Volley se ejecuta en los hilos de fondo. Así que no hay necesidad de implementar sus tareas asíncronas. Puesto que otros han cubierto ya las ediciones en usar Threads no hablaré de ésos. Déjame entrar en el código directamente 🙂

Lo siguiente me ha servido bien cada vez que mis vistas de pantalla, vistas en red o cualquier otra vista dependen de la información de la web:

  1. Crear una interfaz: WebInfoUpdateReceiver.java

     public interface WebInfoUpdateReceiver { public void receive(Foo [] fooItems); } 
  2. Crear una clase para descargar cosas: Downloader.java

     public class Downloader { private Context mContext; private WebInfoUpdateReceiver mReceiver; public Downloader(WebInfoUpdateReceiver receiver) { mReceiver = receiver; } public void downloadStuff() { MyStringRequest request = new MyStringRequest(Request.Method.GET,requestUrl, new Response.Listener<String>() { @Override public void onResponse(String response) { // parse the response into Foo[] mReceiver.update(foo); } } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); RequestQueue queue = Volley.newRequestQueue(mContext); queue.add(request); } } 
  3. Ahora haga que su actividad implemente la interfaz:

     public class Activity extends Activity implements WebInfoUpdateReceiver { public void receive(Foo [] fooItems) { // convert the array into arrayList adapter.insert(arraylist); adapter.notifyDataSetChanged(); } } 

Puede utilizar algo como esto:

  public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder; ... holder.position = position; new ThumbnailTask(position, holder) .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, null); return convertView; } private static class ThumbnailTask extends AsyncTask { private int mPosition; private ViewHolder mHolder; public ThumbnailTask(int position, ViewHolder holder) { mPosition = position; mHolder = holder; } @Override protected Cursor doInBackground(Void... arg0) { // Download bitmap here } @Override protected void onPostExecute(Bitmap bitmap) { if (mHolder.position == mPosition) { mHolder.thumbnail.setImageBitmap(bitmap); } } } private static class ViewHolder { public ImageView thumbnail; public int position; } 

Además, para un rendimiento aún mejor, es necesario agregar la conciencia de interacción a su adaptador ListView para que no desencadene ninguna operación asincrónica por fila después de, por ejemplo, un gesto de fling en el ListView que significa que el desplazamiento es tan rápido que doesn No tiene sentido incluso iniciar cualquier operación asíncrona. Una vez que el desplazamiento se detiene, o está a punto de detenerse, es cuando desea empezar a mostrar realmente el contenido pesado de cada fila.

Un ejemplo muy bueno se puede encontrar aquí: https://code.google.com/p/shelves/

Volley biblioteca parece haber abstraído algunos de los patters comunes para su uso …

Para un control más fino sobre las solicitudes de red y el almacenamiento en caché, puede echar un vistazo a: http://developer.android.com/training/volley/simple.html#send

Al igual que en el diagrama de architechture, al hacer una solicitud, busca la caché, y en caso de falla intenta la solicitud de red y la agrega a la caché para su uso posterior. Además parece que puede proporcionar una solicitud personalizada de reintento que satisface sus necesidades y Manejar / invalidar caché como y cuando sea necesario.

Para una referencia en profundidad que podría echar un vistazo a – https://android.googlesource.com/platform/frameworks/volley/+/master/src/main/java/com/android/volley

IMHO, la mejor práctica sería no realizar operaciones asíncronas en getView (). Su clase del adaptador se supone solamente para transformar objetos en las vistas y usted debe guardarla tan directo como sea posible.

Puede ejecutar problemas si inicia una tarea asíncrona (o un Thread) que hace referencia a la vista recién creada si la tarea finaliza después de que la vista ya no está en la pantalla o si fue reciclada por el adaptador como resultado del desplazamiento.

Debe considerar realizar operaciones asíncronas fuera del adaptador. Inicie una operación y actualice la lista una vez finalizada. Puede actualizar la lista completa o algunos elementos cuando se termina la operación asíncrona.

Las operaciones pueden realizarse en AsyncTask de su actividad o en un servicio dedicado. Evite crear nuevos Threads porque crear subprocesos es costoso y si por alguna razón lo llama demasiado a menudo, puede quedarse sin memoria. AsyncTasks utiliza un grupo de subprocesos y por lo tanto no tendrá este problema.

Por lo tanto, dando un paso atrás y mirando esto completamente de forma abstracta, parece que la pregunta que usted está pidiendo es a lo largo de las líneas de cómo administrar algunas acciones potencialmente largas (red o de otro tipo) que afectarán a la apariencia de un elemento de vista de lista.

Dado este supuesto, se enfrentan a dos problemas principales:

  1. Los hilos son relativamente caros para comenzar
  2. Las vistas devueltas por getView () se reciclan y pueden cambiar "item" cuando el usuario desplaza la lista

La edición 1 puede ser dirigida usando un ThreadPoolExecutor (o cualquier otro mechanisim que usted siente como). La idea es que los hilos se reciclan también, por lo que no toman tanto tiempo para girar.

El número 2 es un poco más complicado. Puede cancelar la operación de hilo cuando la vista está a punto de ser reciclada (hay algunas maneras de hacerlo), pero debe decidir si perder el esfuerzo es aceptable (el usuario puede desplazar su vista de nuevo en la pantalla y tiene que empezar de nuevo). Puede almacenar el resultado de su tarea de larga duración contra el elemento de lista (o algún titular de datos asociado con el elemento de lista), pero debe tener cuidado de quedarse sin memoria (el caché utilizado recientemente o las cachés lru a veces pueden ser útiles aquí ). Esto le permitiría mantener su esfuerzo y actualizar su vista de lista si todavía está en pantalla. Se podría utilizar un indicador en los datos de su elemento para indicar que ya tiene los datos y no volver a cargarlos.

Lo siento, no tengo tiempo suficiente para entrar en más detalles en este momento. Es hora de acostarse para mí 🙂

Buena suerte. Voy a escribir un poco más si tengo tiempo. Saludos cordiales, CJ

  • Acceso de dominio a través de subprocesos con rxjava y dagger2
  • Java.lang.RuntimeException: No se puede crear el controlador dentro de hilo que no ha llamado Looper.prepare ();
  • Android: onCreate () se llama varias veces (y no por mí)
  • Handler.post (runnable) no ejecuta siempre el método run en android
  • Android: brindis en un hilo
  • ¿Cómo implementar la función .get con FutureTask o BackgroundTask usando android?
  • ¿La publicación de Runnable a un hilo de interfaz de usuario garantiza que el diseño esté terminado cuando se ejecuta?
  • ¿Qué sucede con un hilo de Android después de destruir la actividad que lo creó?
  • Hilos y uso de tareas Asynctask para httppost
  • ¿Cuál es la diferencia entre un hilo y un manejador
  • Visibilidad de un botón en el temporizador
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.