In primo luogo il problema:Android: strategia di pool di thread e può essere utilizzato Loader per implementarlo?
- sto lavorando sull'applicazione che utilizza più
FragmentLists
all'interno di una personalizzataFragmentStatePagerAdapter
. Ci potrebbe essere, numero potenzialmente sostanziale di tali frammenti dire tra 20 e 40. - Ogni frammento è un elenco in cui ogni elemento può contenere testo o immagine.
- Le immagini devono essere caricati in modo asincrono dal web e cache di memoria cache temporanea e anche a SD, se disponibile
- Quando Frammento si spegne lo schermo eventuali arrivi e le attività in corso dovrebbero essere annullati (non in pausa)
La mia prima implementazione ha seguito ben noto image loader code da Google. Il mio problema con quel codice è che fondamentalmente crea un'istanza di AsyncTask
per immagine. Che nel mio caso uccide l'app molto velocemente.
Dal momento che sto usando il pacchetto di compatibilità v4 ho pensato che l'utilizzo di Loader personalizzato che si estende AsyncTaskLoader
mi aiuterebbe dal momento che implementa internamente un pool di thread. Tuttavia, a mia spiacevole sorpresa se eseguo questo codice più volte ogni successiva chiamata interromperà il precedente. Dire che ho questo nel mio metodo ListView#getView
:
getSupportLoaderManager().restartLoader(0, args, listener);
Questo metodo viene eseguito in loop per ogni elemento della lista che entra in vista. E come ho affermato - ogni successiva invocazione terminerà la precedente. O almeno questo è quello che succedere basa sulla 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
Poi ho pensato che forse dando ID univoco a ogni caricatore aiuterà le questioni, ma non sembra fare alcuna differenza. Come risultato, finisco con immagini apparentemente casuali e l'app non carica mai nemmeno 1/4 di ciò di cui ho bisogno.
La questione
- quale sarebbe il modo per risolvere il Loader per fare quello che voglio (e c'è un modo?)
- Se non ciò che è un buon modo per creare
AsyncTask
piscina ed è c'è forse un'implementazione funzionante di questo?
Per dare l'idea del codice, ecco la versione ridotta di Loader dove la logica di download/salvataggio è in una classe separata di 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;
}
}
}
'AsyncTask' utilizza già un pool. Il pool sale a 128 threads IIRC, che potrebbe essere la fonte della tua difficoltà. È sempre possibile implementare il proprio pool di thread utilizzando le classi 'java.util.concurrent'. – CommonsWare
Se lo sviluppo è destinato a Android 3.0 (livello API 11), è possibile utilizzare l'API appena aggiunta [AsyncTask.executeOnExecutor()] (http://developer.android.com/reference/android/os/AsyncTask.html#executeOnExecutor% 28java.util.concurrent.Executor,% 20Params ...% 29) controlla bene il pool di thread con il ciclo di vita della creazione AsyncTask. – yorkw
AsynkTask tuttavia può essere eseguito solo una volta, quindi ho bisogno di creare un'istanza per immagine che sto caricando. rendilo 60 e questo è un sacco di oggetti – Bostone