Android – Estrategia de grupo de subprocesos y Loader se puede utilizar para implementarlo?

Primero el problema:

  • Estoy trabajando en la aplicación que utiliza múltiples FragmentLists dentro de un FragmentStatePagerAdapter personalizado. Podría haber, potencialmente un número sustancial de tales fragmentos decir entre 20 y 40.
  • Cada fragmento es una lista en la que cada elemento puede contener texto o imagen.
  • Las imágenes deben cargarse de forma asincrónica desde la web y almacenarse en caché en la memoria caché de la memoria temporal y también en SD si está disponible
  • Cuando el Fragmento se apaga en la pantalla, cualquier carga y actividad actual debe ser cancelada (sin pausar)

Mi primera implementación siguió el código de cargador de imágenes bien conocido de Google. Mi problema con ese código es que básicamente crea una instancia de AsyncTask por imagen. Que en mi caso mata la aplicación muy rápido.

Desde que estoy utilizando el paquete de compatibilidad v4 pensé que el uso de AsyncTaskLoader personalizada que se extiende AsyncTaskLoader me ayudaría ya que internamente implementa un grupo de hilos. Sin embargo a mi desagradable sorpresa si ejecuto este código varias veces cada invocación siguiente interrumpirá el anterior. Digamos que tengo esto en mi método ListView#getView :

 getSupportLoaderManager().restartLoader(0, args, listener); 

Este método se ejecuta en el bucle para cada elemento de lista que aparece en la vista. Y como dije – cada invocación siguiente terminará la anterior. O al menos eso es lo que sucede basado en LogCat

 11-03 13:33:34.910: V/LoaderManager(14313): restartLoader in LoaderManager: args=Bundle[{URL=http://blah-blah/pm.png}] 11-03 13:33:34.920: V/LoaderManager(14313): Removing pending loader: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 11-03 13:33:34.920: V/LoaderManager(14313): Destroying: LoaderInfo{405d44c0 #2147483647 : ImageLoader{405118a8}} 11-03 13:33:34.920: V/LoaderManager(14313): Enqueuing as new pending loader 

Entonces pensé que tal vez dar identificación única a cada cargador ayudará a los asuntos, pero no parece hacer ninguna diferencia. Como resultado acabo con imágenes aparentemente aleatorias y la aplicación nunca carga ni 1/4 de lo que necesito.

La pregunta

  • ¿Cuál sería la manera de arreglar el cargador para hacer lo que quiero (y hay una manera?)
  • Si no lo que es una buena manera de crear piscina AsyncTask y es posible que la aplicación de trabajo?

Para darle la idea del código aquí está reducido la versión de Loader donde la lógica real de descarga / almacenamiento está en una clase separada de ImageManager.

  public class ImageLoader extends AsyncTaskLoader<TaggedDrawable> { private static final String TAG = ImageLoader.class.getName(); /** Wrapper around BitmapDrawable that adds String field to id the drawable */ TaggedDrawable img; private final String url; private final File cacheDir; private final HttpClient client; /** * @param context */ public ImageLoader(final Context context, final String url, final File cacheDir, final HttpClient client) { super(context); this.url = url; this.cacheDir = cacheDir; this.client = client; } @Override public TaggedDrawable loadInBackground() { Bitmap b = null; // first attempt to load file from SD final File f = new File(this.cacheDir, ImageManager.getNameFromUrl(url)); if (f.exists()) { b = BitmapFactory.decodeFile(f.getPath()); } else { b = ImageManager.downloadBitmap(url, client); if (b != null) { ImageManager.saveToSD(url, cacheDir, b); } } return new TaggedDrawable(url, b); } @Override protected void onStartLoading() { if (this.img != null) { // If we currently have a result available, deliver it immediately. deliverResult(this.img); } else { forceLoad(); } } @Override public void deliverResult(final TaggedDrawable img) { this.img = img; if (isStarted()) { // If the Loader is currently started, we can immediately deliver its results. super.deliverResult(img); } } @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); // At this point we can release the resources associated with 'apps' // if needed. if (this.img != null) { this.img = null; } } } 

Ok, así que primero las cosas primero. El AsyncTask que viene con Android no debe ahogar su aplicación o hacer que se bloquee. AsyncTasks se ejecutan en un grupo de subprocesos donde hay como máximo 5 subprocesos en ejecución al mismo tiempo. Mientras que usted puede hacer cola para arriba muchas tareas para ser ejecutado, solamente 5 de ellos están ejecutando a la vez. Al ejecutar estos en el threadpool de fondo que no debe tener ningún efecto en su aplicación en absoluto, que debe funcionar sin problemas.

El uso de AsyncTaskLoader no resolvería su problema si no está satisfecho con el rendimiento del cargador AsyncTask. El AsyncTaskLoader sólo toma la interfaz del cargador y se casa con un AsyncTask. Así que es esencialmente cartografía onLoadFinished -> onPostExecute, onStart -> onLoadInBackground. Así que es lo mismo.

Utilizamos el mismo código de cargador de imágenes para nuestra aplicación que hace que un asynctask se coloque en la cola de subprocesos cada vez que intentamos cargar una imagen. En el ejemplo de google, asocian la vista de imagen con su tarea asíncrona para que puedan cancelar la tarea asíncrona si intentan reutilizar la vista de imagen en algún tipo de adaptador. Usted debe tomar una estrategia similar aquí. Debe asociar su imagen con la tarea asíncrona al cargar la imagen en segundo plano. Cuando tenga un fragmento que no esté mostrando, podrá recorrer las vistas de imagen asociadas con ese fragmento y cancelar las tareas de carga. Simplemente usando el AsyncTask.cancel () debería funcionar lo suficientemente bien.

También debe intentar implementar el mecanismo de caché de imágenes sencillo que el ejemplo de vista de imagen asíncrona detalla. Simplemente creamos un hashmap estático que va de url -> weakreference. De esta manera las imágenes se pueden reciclar cuando tienen que ser porque sólo se mantienen con una referencia débil.

Aquí está un esquema de la carga de la imagen que hacemos

 public class LazyLoadImageView extends ImageView { public WeakReference<ImageFetchTask> getTask() { return task; } public void setTask(ImageFetchTask task) { this.task = new WeakReference<ImageFetchTask>(task); } private WeakReference<ImageFetchTask> task; public void loadImage(String url, boolean useCache, Drawable loadingDrawable){ BitmapDrawable cachedDrawable = ThumbnailImageCache.getCachedImage(url); if(cachedDrawable != null){ setImageDrawable(cachedDrawable); cancelDownload(url); return; } setImageDrawable(loadingDrawable); if(url == null){ makeDownloadStop(); return; } if(cancelDownload(url)){ ImageFetchTask task = new ImageFetchTask(this,useCache); this.task = new WeakReference<ImageFetchTask>(task); task.setUrl(url); task.execute(); } ...... public boolean cancelDownload(String url){ if(task != null && task.get() != null){ ImageFetchTask fetchTask = task.get(); String downloadUrl = fetchTask.getUrl(); if((downloadUrl == null) || !downloadUrl.equals(url)){ fetchTask.cancel(true); return true; } else return false; } return true; } } 

Así que simplemente gire a través de las vistas de imagen que están en su fragmento y luego cancelarlos cuando su fragmento se esconde y mostrarlos cuando su fragmento es visible.

  • ¿Es una buena idea que un Fragmento delegue todo el control de navegación en Actividad?
  • Mostrar / ocultar el teclado virtual de Android en el fragmento reemplazado
  • Escáner de código de barras ZXing en diseño personalizado en fragmento
  • ¿Cuál es la diferencia entre el fragmento y la actividad? ¿Qué código se puede escribir en fragmento?
  • ¿Cuál es la diferencia entre usar add (). AddToBackStack (), add (). Detach () y replace (). AddToBackStack () en un FragmentTransaction?
  • Android FragmentTransaction commit ya se ha llamado
  • Fragmento Transacciones con transición - Los nombres de transición únicos son necesarios
  • Android: Solución para support.v4.app.Fragment -> Fragment classcastexception?
  • Accesibilidad de Android talkback para decir el título del fragmento
  • Android cómo saber si el menú deslizante está visible
  • Al hacer clic en el botón tras una transacción Fragment utilizando addToBackStack no hace nada
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.