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


Por favor, no cierre esto, en mi humilde opinión es una pregunta de programación decente y posiblemente útil.


Por favor, estoy leyendo muchas cosas y me estoy confundiendo porque leo opiniones y enfoques diferentes.

El problema es el siguiente:

en el getView() de un AdapterNecesito realizar alguna operación asíncrona, como verificar una formación en la web y actualizar la vista en función de eso.

Utilicé el siguiente enfoque:

cada vez getView() se llama empiezo un Thread

pero mi enfoque me ganó muchas 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 estoy buscando una solución para ejecutar las solicitudes de red en getView(), sino más bien, cómo actualizar la vista en función del resultado de la llamada asíncrona.


5
2018-02-13 16:45


origen


Respuestas:


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

  1. AsyncTask 

    • La agrupación de hilos aquí se realiza internamente, por lo que no necesita molestarse con eso
    • Es un enfoque más limpio para su problema en lugar de generar subprocesos individuales.
    • Si su usuario cambia la pantalla durante su llamada a la API, también puede cancelar la llamada.
    • Tendrías que habilitar notifyDatasetChanged ()
    • Debe anular muy pocas funciones para lograr la funcionalidad que desea.
  2. AsyncTaskLoader

    • Te da mas control pero pierde en varias funciones implícitamente definidas
    • Necesita más conocimiento para usar esto y debe estar bien versado en clases como LoaderManager,Cargador.
    • el cambio es auto-disparador Diga que si cambiara su conjunto de datos subyacentes, los cambios se activarán automáticamente y brindarán un cambio en su interfaz de usuario.
  3. Manipuladores e hilos

    • Esta es una situación por encima de su enfoque actual, pero proporciona más beneficios
    • Puede abstraer la creación del hilo y proporcionar un controlador que maneje la llamada de todos sus identificadores.
    • Puede poner en cola los hilos y los mensajes entregados.
    • si la pantalla cambia, podrías eliminar llamadas y 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 "notable por el usuario" sería el primero.

Hay y podría haber varios otros enfoques basados ​​en el control que necesita y su experiencia con las devoluciones de llamadas de Android y la gestión de subprocesos. Pero estos tres son (según mi opinión) los más adecuados.

PD: 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 podrías mirar DataSetObserver para automatizar nuevamente sus requerimientos.


2
2018-02-16 06:50



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

El mejor consejo que puedo darte es buscar los datos de antemano. Tire de los datos, actualice el ArrayList Que tu Adapter está conectado a, a continuación, llame adapter.notifyDataSetChanged(). Esto volverá a dibujar toda su información.

Extraiga todos los datos a la vez, no en partes pequeñas. Esta es la forma mejor y más razonable de hacer esto.


5
2018-02-16 05:02



EDITAR

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

Google I / O 2013 : P Te sugiero que veas esta I / O de Google desde 2013. Han explicado claramente muchas de estas cosas allí. Todas sus preguntas serán respondidas allí. Es canon

He usado la biblioteca de Volley aquí. Si lees los documentos, verás que Volley se ejecuta en subprocesos de fondo. Así que no hay necesidad de implementar sus tareas asíncronas. Dado que otros ya han cubierto los problemas relacionados con el uso de Threads, no hablaré de ellos. Déjame entrar en el código directamente :)

Lo siguiente me ha servido bien cuando mis vistas de lista o de cuadrícula 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. Crea 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 haz que tu 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();
    }
      }
    

4
2018-02-23 01:29



Puedes usar 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 obtener un rendimiento aún mejor, debe agregar la conciencia de interacción a su ListView adaptador para que no dispare ninguna operación asincrónica por fila después de, digamos, un gesto de lanzamiento en el ListView- lo que significa que el desplazamiento es tan rápido que no tiene sentido ni siquiera comenzar una operación asíncrona. Una vez que se detiene el desplazamiento, o está a punto de detenerse, es cuando desea comenzar a mostrar el contenido pesado de cada fila.

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


1
2018-02-20 06:41



Volley library parece haberse abstraído de algunos de los patrones comunes para su uso ...

Para un mejor control 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 arquitectura, al realizar una solicitud, busca la memoria caché y, en caso de fallas, intenta la solicitud de red y la agrega a la memoria caché para su uso posterior. Además, parece que puede proporcionar una solicitud personalizada de reinserción para satisfacer sus necesidades y manejar / invalidar la memoria caché cuando sea necesario.

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


1
2018-02-21 16:19



En mi humilde opinión, la mejor práctica sería no realizar operaciones asíncronas en getView (). Se supone que su clase de Adaptador solo transforma los Objetos en Vistas y usted debe mantenerla lo más directa posible.

Puede tener problemas si inicia una tarea asíncrona (o un Subproceso) 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 el adaptador la recicló como resultado del desplazamiento.

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

Las operaciones mismas se pueden realizar en AsyncTask de su actividad o en un Servicio dedicado. Evite crear nuevos subprocesos porque crear subprocesos es costoso y, si por alguna razón lo llama con demasiada frecuencia, puede quedarse sin memoria. AsyncTasks usa un grupo de subprocesos y, por lo tanto, no tendrá este problema.


1
2018-02-21 17:05



Por lo tanto, dando un paso atrás y mirando esto de manera completamente abstracta, parece que la pregunta que está haciendo es sobre cómo administrar una acción potencialmente larga (red u otra) que afectará la apariencia de un elemento de vista de lista.

Dado ese supuesto, te enfrentas a dos problemas principales:

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

El problema 1 se puede solucionar utilizando un ThreadPoolExecutor (o cualquier otro mecanismo que desee). La idea es que los hilos también se reciclen, por lo que no tardan mucho tiempo en girar.

El número 2 es un poco más complicado. Puede cancelar la operación de subprocesos cuando la vista está a punto de reciclarse (hay algunas formas de hacerlo), pero debe decidir si es aceptable perder el esfuerzo (el usuario puede desplazar la vista hacia atrás en la pantalla y tiene que empezar de nuevo). Puede almacenar el resultado de su tarea larga en el elemento de la lista (o algún soporte de datos asociado con el elemento de la lista) pero debe tener cuidado de quedarse sin memoria (el caché utilizado menos recientemente o las memorias caché lru a veces pueden ser útiles aquí ) Esto le permitirá mantener su esfuerzo y solo actualizar su vista de lista si aún está en la pantalla. Se podría usar una marca en los datos de tu artículo para indicar que ya tienes 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 dormir para mí :-)

Buena suerte. Escribiré un poco más si tengo tiempo. Saludos cordiales, CJ


1
2018-02-22 08:59