2012-09-12 9 views
6

È 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:

  1. 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.

  2. 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.

+0

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

risposta

8

perché la tua attività non avvia un servizio per ospitare un'attività a esecuzione prolungata? un servizio è la soluzione migliore per mantenere le cose in esecuzione a lungo termine. puoi fornire suggerimenti al sistema operativo per lasciarlo in esecuzione. vedi startForeground() e START_STICKY.

è possibile comunicare di nuovo all'attività trasmettendo dal servizio. fai in modo che la tua attività registri a livello di programmazione un broadcast receiver per ascoltare quegli intenti. se l'attività è in pausa, annulla la registrazione del tuo ricevitore, in questo modo non risponderai quando non sarai in primo piano.

se il sistema operativo distrugge il vostro servizio, quindi sta uccidendo il processo, quindi il vostro compito è condannato comunque. il meglio che puoi fare è riavviarlo. comunque, questo non accadrà se non nelle condizioni più estreme se si considera quanto sopra.

MODIFICA: riassumendo alcuni pensieri catturati nei commenti ... mantenendo un processo di lavoro a lunga esecuzione e riavviandolo automaticamente è quasi sempre la cosa sbagliata da fare. i dispositivi mobili non hanno la fonte di alimentazione per renderlo fattibile. l'autore ha dichiarato che ha una condizione speciale che nega questa preoccupazione ... che sarebbe vero solo se il dispositivo è collegato a una fonte di energia quasi sempre.

il modello di Android è quello di fornire un robusto set di intenti (notifiche) a cui l'applicazione può ascoltare. questi intenti svegliano il tuo dispositivo/applicazione quando è il momento di fare qualcosa. la tua app dovrebbe gestire la notifica e quindi consentire al dispositivo di tornare immediatamente in stato di stop.

se non ci sono intenzioni di magazzino che mappano l'evento, è possibile utilizzare Google Cloud Messaging per riattivare i dispositivi da un server.in altre parole, lasciare che i processi a esecuzione prolungata vengano eseguiti sul server e riattivare il dispositivo quando richiesto.

se è possibile farlo, un'alternativa sarebbe quella di utilizzare AlarmManager per svegliarsi periodicamente e controllare alcuni stati. anche se lo facevi spesso (ad es. ogni 5 minuti, che è anche un no-no), sarebbe molto meglio che tenere il dispositivo sempre attivo come suggerito. inoltre, utilizzare intervalli di sveglia non esatti (consultare la documentazione collegata sopra).

+0

Sì, esegui come servizio, e fai come alcune app, aggiungi un'icona nella barra di prova ti aiuterà anche a prevenire l'uscita del servizio, puoi ottenere un evento mentre un'app per Android si fermerà. –

+0

È incredibile come un semplice thread di lavoro che avrei codificato come classe Runnable nidificata in qualsiasi altra piattaforma, in Android coinvolge i servizi FG, i ricevitori e forse i lock per la sveglia. –

+0

I processi in background di lunga durata sono un caso speciale su qualsiasi piattaforma mobile. semplicemente non vuoi farlo nella maggior parte dei casi, perché i dispositivi mobili non hanno la fonte di alimentazione per renderlo fattibile. –