sabato 26 febbraio 2011

Android, stati e metodi di callback per un'activity

Un'activity, dunque, descrive attraverso una schermata una possibile interazione per l'utente. Poiché in Android il focus spetta a una sola activity, il sistema operativo attribuisce alle attività diversi stati. Ognuno di questi contribuisce allora alla definizione di un ciclo di vita per le applicazioni. L'applicazione, quindi l'activity che la rappresenta in quel momento, viene condotta da uno stato a un altro attraverso dei metodi di callback. Un'activity può trovarsi in uno dei seguenti stati:
  • ACTIVE: l'activity è visibile all'utente che può quindi interagire con la stessa;
  • PAUSED: l'activity non è attiva e può essere visibile in parte (attraverso effetti di trasparenza);
  • STOPPED: l'activity non è né attiva e né visibile;
Se l'activity si trova nello stato PAUSED o STOPPED può essere terminata dal sistema operativo (attraverso il metodo finish()) per liberare le risorse impegnate a favore dell'activity in esecuzione (nello stato ACTIVE). Se la stessa activity viene poi richiesta dall'utente il sistema operativo procederà alla sua creazione, ripristinando i valori salvati.
Un'activity può trovarsi nello stato ACTIVE subito dopo l'istanza della stessa, attraverso i metodi onCreate(), onStart() e onResume(), eseguiti in successione. L'esecuzione di un'altra activity pone l'activity fin li usata dall'utente nello stato PAUSED, attraverso il metodo onPause(), ed esegue una nuova activity (attraverso la sequenza di metodi detti prima) oppure ne ripristina una già esistente. Tale ripristino può avvenire invocando il metodo onResume(), se l'applicazione da ripristinare è nello stato PAUSED, oppure il metodo onRestart() (seguito dai metodi onStart() e onResume()) se l'applicazione è invece nello stato STOPPED. Un'activity transita dallo stato PAUSED allo stato STOPPED attraverso il metodo onStop(). Per garantire all'utente la disponibilità dell'activity richiesta la transizione da PAUSED a STOPPED non avviene immediatamente. Il sistema operativo, infatti, ferma l'activity in esecuzione facendola transitare da RUNNING a PAUSED, quindi avvia l'activity richiesta dall'utente e solo dopo si preoccupa di ultimare il lavoro, fermando la precedente activity da PAUSED a STOPPED. Un'activity nello stato STOPPED, infine, rilascia le risorse impegnate attraverso il metodo onDestroy().


Metodi di callback:
  • onCreate(): viene chiamato quando l'activity è creata per la prima volta. E' qui che invocando il metodo findViewById() possiamo intercettare i componenti inseriti nel layout, con setContentView(), e programmarne i comportamenti. L'oggetto Bundle permette di ripristinare lo stato dell'activity, se quest'ultima è stata chiusa;
  • onStart(): viene invocato prima che l'activity diventi visibile all'utente;
  • onResume(): viene invocato prima che l'activity possa interagire con l'utente;
  • onPause(): viene invocato quando un'activity sta per essere messa in background, prima di ripristinare l'activity chiesta dall'utente. E' preferibile non appesantire questo metodo poiché finché esso non termina il sistema operativo non avvia l'activity richiesta! Meglio salvare lo stato con onSaveInstanceState(Bundle obj);
  • onStop(): viene invocato dal sistema operativo quando l'applicazione non è più usata dall'utente;
  • onDestroy(): viene invocato prima di terminare l'applicazione. Può essere invocato dall'utente per terminare l'applicazione (metodo finish()) o dal sistema operativo per liberare risorse;
  • onRestart(): viene chiamato prima del metodo onStart() per ripristinare un'activity dallo stato STOPPED;
  • onSaveInstanceState(Bundle obj): viene invocato per salvare informazioni sullo stato dell'activity che verrà fermata;
  • onRestoreInstanceState(Bundle obj): viene invocato per ripristinare i valori salvati;
Un'activity di un'applicazione non necessariamente deve implementare tutti i metodi di callback. Il ciclo di vita di un'applicazione Android può essere verificato attraverso un'applicazione che sovrascrivendo tutti i metodi di callback detti sopra mostri all'interno della finestra LogCat (di Eclipse) l'alternanza dei metodi coinvolti nell'utilizzo dell'applicazione. Trovate qui tale applicazione, due activity che regolano il valore di un contatore. La prima permette di aumentarne il valore attraverso la pressione di un bottone, oppure di avviare la seconda activity. Un altro bottone permette di chiudere l'applicazione. La seconda attività eredita dalla precedente activity lo stato del contatore e ne diminuisce il valore, oltre a permettere come la prima la chiusura dell'applicazione.
La stampa di messaggi di log avviene invocando nei metodi di callback il metodo Log, del package android.util.Log. A tale metodo occorre passare una stringa per etichettare con un tag i messaggi (variabile tag delle due activity) e una stringa che rappresenterà il messaggio da stampare. Ogni metodo di callback deve necessariamente chiamare il metodo della classe padre (per l'ereditarietà), con la parola chiave super. I bottoni dei layout applicati alle activity vengono rintracciati attraverso il metodo findViewById(). Nei metodi onSaveInstanceState(Bundle outState) e onRestoreInstanceState(Bundle savedInstanceState) vengono, rispettivamente, salvati e ripristinati i valori usati nell'applicazione. Il passaggio da un activity a un'altra avviene attraverso la definizione di un Intent. Ogni activity è in grado di svolgere un determinato lavoro e può dunque rispondere all'esigenza di un'altra applicazione in esecuzione. Questo permette di riutilizzare componenti già scritti! Nel codice dell'applicazione il passaggio alla seconda activity avviene con le seguenti istruzioni:
Intent intent=new Intent(LifecycleActivity.this,LifecycleSubActivity.class);
intent.putExtra("status",status);
startActivity(intent)
dove viene passato anche lo stato del contatore, con putExtra(). La seconda activity va dichiarata all'interno del manifesto dell'applicazione, attraverso il tag activity. L'attributo android:name del tag xml specifica il nome dell'activity da aggiungere all'applicazione. Avviando l'activity e seguendo l'output della finestra LogCat possiamo subito confermare l'invocazione, in sequenza, dei metodi onCreat(), onStart() e onResume().


Vi consiglio di applicare un filtro all'output di LogCat, usando proprio la stringa tag definita nelle due activity! Cliccando sul bottone exit leggeremo l'invocazione dei metodi onPause(), onStop() e onDestroy().


Avviamo nuovamente l'applicazione, modifichiamo il valore del contatore e invochiamo adesso la seconda activity (con il bottone Del). Nel LogCat leggeremo, come prima, i metodi della prima activity, quindi noteremo la presenza di onPause() per la prima activity e subito dopo i metodi onCreate(), onStart() e onResume() per la seconda activity. Per la prima activity leggeremo ora la presenza del metodo onStop().


Premendo il tasto back del dispositivo leggeremo la presenza di onPause() per la seconda activity, quindi onRestart(), onStart() ed infine onResume() per la prima activity (che è già stata creata prima). Per la seconda activity leggeremo, poi, la presenza di onStop(). Nell'output di LogCat noteremo inoltre la presenza dei metodi per salvare e ripristinare la variabile contatore. La seconda activity, infatti, non salva la variabile modificata (modificate il codice affinché ciò avvenga).

7 commenti:

  1. ci sono 2 piccoli errori nel disegno.

    dallo stato paused non viene chiamato il metodo onCreate() {l'activity è già in memoria}

    quella freccia deve partire dallo stato DESTROYED.

    un activity prima di passare dallo stato paused allo stato inactive (che tu hai segnato come destroyed) deve per forza essere invocato il metodo onStop()

    per il resto complimenti per i contenuti.

    RispondiElimina
  2. Grazie per la correzione, ho modificato il disegno ;)

    RispondiElimina
  3. Ciao, ottimo articolo.

    Sto sviluppando una piccola app fatta da una ventina di activity, in ciascuna di esse ho messo una textview e una scrollview (perchè il testo è lungo e bisogna per forza scrollare per leggerlo tutto). Il testo finisce con un pulsante che ti manda alla activity successiva.

    Vorrei implementare un bookmark che salvi l'activity a cui l'utente è arrivato a leggere, per poi richiamarla quando l'utente lancia l'app nuovamente...

    Spero di essere stato chiaro, potreste indirizzarmi su quali argomenti dovrei studiare per poter fare una lavoro di questo tipo?

    RispondiElimina
  4. Ciao,
    secondo me dovresti studiare come salvare informazioni utili per la tua applicazione sulle "SharedPreferences". L'activity avviata al click sull'icona mostrata nel launcher è quella dichiarata come MAIN all'interno del "manifesto" xml che descrive la tua applicazione. Salvando sulle "SharedPreferences" l'ultima activity vista e ripristinandola all'avvio dell'applicazione potresti secondo me riuscire a fare quello che descrivi.

    Sull'evento onPause dell'activity che ha il focus al momento dell'uscita salvi sulle "SharedPreferences" il nome della classe. Sull'evento onCreate dell'activity MAIN leggi il none della classe/activity da ripristinare.

    RispondiElimina
    Risposte
    1. grazie, provo poi ti faccio sapere :)

      Elimina
    2. scusa ma perchè mi consigli sharedPreferences e non il metodo onSaveInstanceState?

      Elimina
  5. Ciao,
    come ti dicevo, leggendo il tuo scenario ho pensato a una possibile soluzione con le SharedPreferences: ogni activity che viene visitata dall'utente scrive il nome della propria classe in una variabile condivisa. Tale variabile verrebbe poi letta sull'activity main della tua app, in modo da poter richiamare l'ultima app che era in esecuzione (immagina uno switch/case nel codice dell'app main, che lancia mediante opportuna intent, l'ultima app visitata).
    Questo comportamento potresti realizzarlo anche con i metodi onSaveIstanceState/onRestoreInstanceState che, se ricordo bene, vengono chiamati solo in opportuni eventi (dovrei verificare). Dovresti sovrascrivere i due metodi per descrivere cosa salvare/ripristinare (cosa cioè è importante per la tua app).
    Personalmente userei onSaveIstanceState/onRestoreInstanceState per salvare/ripristinare i valori scritti nella GUI dell'app (e quindi lo stato dell'activity in tal senso).
    Fammi sapere come risolvi per la tua app ;)

    RispondiElimina