Ho studiato questo problema da mesi ormai, ho trovato diverse soluzioni ad esso, di cui non sono contento poiché sono tutti hack enormi. Non riesco ancora a credere che una classe imperfetta nel design sia entrata nel framework e nessuno ne parli, quindi immagino che mi debba mancare qualcosa.
Il problema è con AsyncTask
. Secondo la documentazione esso
"consente di eseguire operazioni in background e pubblicare risultati sul thread dell'interfaccia utente senza dover manipolare thread e / o gestori."
L'esempio continua quindi a mostrare come showDialog()
viene chiamato un metodo esemplare onPostExecute()
. Questo, tuttavia, mi sembra del tutto inventato , perché mostrare una finestra di dialogo ha sempre bisogno di un riferimento a un valido Context
e un AsyncTask non deve mai contenere un forte riferimento a un oggetto di contesto .
Il motivo è ovvio: cosa succede se l'attività viene distrutta e ciò ha innescato l'attività? Questo può succedere sempre, ad esempio perché hai capovolto lo schermo. Se l'attività conterrà un riferimento al contesto che lo ha creato, non stai solo trattenendo un oggetto di contesto inutile (la finestra sarà stata distrutta e qualsiasi interazione dell'interfaccia utente non riuscirà con un'eccezione!), Rischi anche di creare un perdita di memoria.
A meno che la mia logica non sia difettosa qui, questo si traduce in: onPostExecute()
è del tutto inutile, perché a che serve questo metodo eseguire sul thread dell'interfaccia utente se non si ha accesso a nessun contesto? Non puoi fare nulla di significativo qui.
Una soluzione alternativa sarebbe quella di non passare istanze di contesto a un AsyncTask, ma a Handler
un'istanza. Funziona così: dal momento che un gestore si lega vagamente al contesto e all'attività, è possibile scambiare messaggi tra di loro senza rischiare una perdita (giusto?). Ciò significherebbe che la premessa di AsyncTask, vale a dire che non è necessario preoccuparsi dei gestori, è sbagliata. Sembra anche abusare di Handler, poiché stai inviando e ricevendo messaggi sullo stesso thread (lo crei sul thread dell'interfaccia utente e lo invii in onPostExecute () che viene eseguito anche sul thread dell'interfaccia utente).
Per finire, anche con quella soluzione alternativa, hai ancora il problema che quando il contesto viene distrutto, non hai alcuna registrazione delle attività che ha generato. Ciò significa che è necessario riavviare qualsiasi attività quando si ricrea il contesto, ad esempio dopo una modifica dell'orientamento dello schermo. Questo è lento e dispendioso.
La mia soluzione a questo (come implementato nella libreria Droid-Fu ) è quella di mantenere una mappatura di WeakReference
s dai nomi dei componenti alle loro istanze correnti sull'oggetto applicazione univoco. Ogni volta che viene avviato un AsyncTask, registra il contesto di chiamata in quella mappa e su ogni richiamata, recupera l'istanza di contesto corrente da quella mappatura. Ciò garantisce che non si farà mai riferimento a un'istanza di contesto obsoleta e che si abbia sempre accesso a un contesto valido nei callback in modo da poter svolgere un lavoro di interfaccia utente significativo lì. Inoltre non perde, perché i riferimenti sono deboli e vengono cancellati quando non esiste più alcuna istanza di un determinato componente.
Tuttavia, è una soluzione complessa e richiede di sottoclassare alcune delle classi di librerie Droid-Fu, rendendolo un approccio piuttosto invadente.
Ora voglio semplicemente sapere: mi sto perdendo qualcosa in modo massiccio o AsyncTask è davvero completamente imperfetto? In che modo le tue esperienze ci lavorano? Come hai risolto questi problemi?
Grazie per il tuo contributo.