È noto che Android colloca i componenti delle applicazioni (attività, servizi) sotto la minaccia di essere uccisi in qualsiasi momento. Ciò rende le cose davvero complicate se si desidera fornire una soluzione robusta e senza perdite, mantenendo allo stesso tempo il codice pulito e affrontando la separazione delle preoccupazioni.Good Design: thread di worker e attività riavviati
Problema:
Un'attività avvia un dispendio di tempo (in forma di un Runnable
, AsyncTask
o qualsiasi altro meccanismo). Dovrebbe essere visualizzata una finestra di dialogo di avanzamento. L'attività aprirà diverse connessioni, ma dovrebbe avere un mezzo per aggiornare una barra di avanzamento e potrebbe essere necessario visualizzare una finestra di dialogo di scelta a metà del completamento. Dovrebbe anche essere in grado di chiudere la finestra di dialogo e mostrare un Toast quando si verifica un errore o quando si completa.
quando un'attività viene ucciso e poi una nuova istanza viene ricreato, posso pensare a due opzioni:
Prova a uccidere il compito di corsa pure, e generare un'altra istanza di attività in caso di nuove sostituto l'istanza di attività è creata. Questa non è un'opzione per un'attività di lunga durata, poiché è inaccettabile dal punto di vista dell'utente avviare l'attività e vedere l'indicatore della barra di avanzamento passare dall'80% allo 0% senza una ragione apparente. Questo tuttavia è l'approccio seguito nel Shelves example by Romain Guy. Apparentemente questo è l'approccio raccomandato nella Guida per gli sviluppatori ufficiale.
Mantenere il compito in esecuzione: in modo che potessimo avere un'attività sottoscrizione (ed eventualmente di partenza) il compito per gli aggiornamenti a
onResume
, e cancellarsi (e possibilmente pausa) è a .
In seguito alla seconda opzione, è necessario impedire che il compito venga raccolto nel caso in cui l'attività venga temporaneamente distrutta. Ad esempio, potremmo dichiararlo all'interno di una classe di applicazione personalizzata, o anche offrirlo come servizio al resto dell'applicazione (utilizzando un servizio Android Singleton?). Non ne sono certo fan, dato che l'attività è usato solo dall'attività, quindi non ha senso renderlo disponibile ad altre classi. I servizi Android, d'altra parte, devono anche gestire il ciclo di vita.
L'opzione n. 2 implica anche che l'attività precedente non sia trapelata. Non dovremmo provare ad aggiornare la GUI se non è presente alcuna attività. L'intero pensiero dovrebbe essere thread-safe. Infine, l'attività non dovrebbe rimanere in memoria dopo che siamo sicuri che non è più necessaria. Ma allo stesso tempo, se l'attività iniziale viene distrutta, l'attività continua a funzionare e viene avviata una nuova attività sostitutiva, l'attività deve essere in corso per aggiornare immediatamente la GUI e visualizzare lo stato corrente dell'attività (o comporterà se è completata).
Il fatto che l'attività possa essere eseguita quando non viene mostrata alcuna attività è un altro problema da affrontare, dal momento che potremmo aver bisogno dell'interazione dell'utente (finestra di scelta) in modo sincrono ad un certo punto. Questo potrebbe essere risolto se l'attività ha la capacità di sospendere immediatamente l'attività quando raggiunge , ma l'operazione potrebbe richiedere del tempo per fermarsi o addirittura non essere in grado di farlo.
So che domande simili sono già state fatte prima, ma non sto chiedendo specificamente come risolvere il problema, ma su quale progetto sarebbe consigliato per ottenere un accoppiamento lento e isolare l'attività dal compito tanto quanto possibile.
In breve:
- Questo è un problema ricorrente nello sviluppo di Android e qualcuno probabilmente ha messo a punto un design intelligente prima. Esiste un modello di progettazione o una libreria ben testata che semplifica questo?
- Se dovessi implementare il # 2, quale classe sceglieresti per l'attività (Runnable, Service, ecc.) E come comunicherà con l'attività?
P.S. Si prega di astenersi dal pubblicare soluzioni basate sulla modifica "orientation | keyboardHidden" XD. Oltre a questo, ogni aiuto sarebbe apprezzato.
UPDATE:
Il mio primo tentativo ha un po 'confuso. L'app è un'utilità di stampa bluetooth, che prevede il rilevamento dello stato BT (e chiede all'utente di abilitarlo se fosse disabilitato), il recupero dei dispositivi associati (e la richiesta all'utente di selezionarne uno se ce ne fosse più di uno), quindi l'invio dei dati e infine informare l'utente sul risultato. Poiché questo compito era composto da 80% di codice IO, 20% di operazioni relative alla GUI, dovevo raggruppare il codice della GUI all'inizio e alla fine dell'attività, quindi spostarlo dall'attività all'attività. Ho un IntentService
che esegue il sollevamento pesante, quindi riporta all'attività tramite BroadcastReceiver
. Questo risolve la maggior parte dei problemi, ma ci sono alcuni problemi con un tale progetto. Innanzitutto, ci sono tutte quelle stringhe costanti usate come chiavi per mettere e recuperare i campi dagli Intent di input e output, che introducono l'accoppiamento semantico. Avrei preferito la sicurezza del tipo nell'attività < -> comunicazione di servizio. E devo ancora risolvere come passare oggetti personalizzati complessi al servizio nell'Intent, ormai sono bloccato con Parcelables e parametri primitivi. Infine, sia l'attività che il servizio utilizzano la stessa API (bluetooth), avrei preferito avere il codice relativo a BT in una singola classe. Penso che getterò via questo disegno e riproverò con una coda di stampa personalizzata basata sui thread, nonostante ciò renderà più difficile gestire le attività uccise.
AsyncTask è in pausa quando l'attività viene ricreata. I messaggi che l'attività utilizza per il progresso/risultato non vengono consegnati mentre l'attività viene distrutta (può ancora accadere dopo una pausa/prima di riprendere). L'uso dell'istanza non di configurazione per passare attorno ad AsyncTask tra le istanze di attività dovrebbe funzionare abbastanza bene. – zapl