Senza categoria

[DEV] – Activity e ciclo di vita

Una activity è il componente fondamentale attraverso il quale una applicazione interagisce con l’utente. Può a tutti gli effetti essere paragonata ad una form dei linguaggi tradizionali (visual basic) o ad una semplice pagina html visualizzata da un browser (senza immaginare frame e/o componenti ajax).

Così come un’applicazione web porta l’utente da una pagina HTML all’altra, così un’applicazione android porta l’utente da un’activity all’altra. La differenza (nell’analogia con il web) sta che nelle applicazioni web si può saltare da una pagina all’altra mentre nelle applicazioni web le activity seguono una logica “a stack”.

In pratica android, per tutte le applicazioni, tiene traccia della sequenza di visualizzazione delle activity in uno stack. Ogni volta che una applicazione chiede di visualizzare una activity questa viene posta in cima allo stack e quella che era precedentemente visualizzata diventa la penultima.

All’interno di una applicazione, il programmatore, fa navigare l’utente da una activity ad un’altra (della stessa applicazione) visualizzando quella che, di volta in volta serve in base alle esigenze dell’applicazione, ma non è detto che tutte le activity appartengono alla stessa applicazione, infatti lo stack è unico e condiviso. Per vederne un’esempio reale, provate ad aprire un’applicazione qualsiasi (tipo la calcolatrice). Questa diventerà quella in cima allo stack delle activity, poi (senza premere il tasto back) tenete premuto home e lanciate una qualsiasi delle applicazioni aperte recentemente. La calcolatrice diventerà la penultima e l’applicazione lanciata verrà messa in cima allo stack. Ora se premete back, l’applicazione si chiuderà e la calcolatrice ritornerà ad essere quella in cima allo stack.

Quindi, in una applicazione le activity si susseguono l’una all’altra secondo la pila dello stack. E’ importante capire il ciclo di vita di una activity per poter gestire questo flusso di messaggi (poni in cima allo stack, metti in pausa l’activity) che il sistema android invia alla nostra applicazione.

Prima di vedere il ciclo di vita di un’activity definiamo quali sono gli stati in cui una qualsiasi activity si può trovare in un dato instante indipendentemente dal fatto che sia in cima allo stack o meno.

Android definisce quattro stati :

  • INACTIVE: Un’activity in questo stato è pronta per essere eseguita. Questo è anche lo stato in cui un’activity si trova quando viene distrutta dal sistema.
  • RUNNING: Quando un’activity è in questo stato significa che è in cima allo stack (e quindi in ogni istante ci può essere solo un’activity in questo stato). Android non terminerà mai il processo legato a quest’activity, (a costo di morire) anche in condizioni di pochissime risorse disponibili.
  • PAUSED: Un’activity si trova in questo stato quando è la penultima dello stack ed è ancora parzialmente visibile. Cioè quando l’activi principale è una dialog o ha un effetto trasparenza. In pratica l’activity in questo stato era quella RUNNING prima di lanciare una nuova activity che però non ricopre completamente la vecchia.
  • STOPPED: Una activity in questo stato non è più visibile e quindi può essere candidata per la rimozione

E’ importante sapere lo stato in cui l’activity si trova perché android, in caso di scarse risorse disponibili può o meno decidere di chiudere una o più activity per liberare risorse. Le activity che mai cercherà di chiudere sono quelle in stato di RUNNING, mentre quelle che cercherà di chiudere per ultime sono quelle in stato PAUSED. Le activity in stato STOPPED e INACTIVE possono essere eliminate tranquillamente.

Vediamo ora il ciclo di vita di un’activity, come si legano ai vari stati e quali sono i metodi di callback messi a disposizione dalla classe base (Activity) per sua gestione commentado dei casi classici.

Vediamo come si comporta in casi reali:

Caso semplice: Visualizzazione e back

Quando viene lanciata un’activity per la prima volta, questa parte dallo stato INACTIVE e vengono invocati in successione i messaggi (metodi) onCreate(), onStart() e onResume(). A questo punto l’activity è visibile sullo schermo (stato RUNNING) e l’utente può interagire con essa.

L’activity rimane in questo stato fino a quando non avviene una delle due cose:

  • L’utente preme back (volendo tornare alla schermata / activity precedente)
  • Viene visualizzata un’altra activity, che può voler dire che l’utente ha premuto un bottone per visualizzare un’altra activity (operazione voluta) o magari è in arrivo un telefonata e compare l’activity del telefono (operazione non voluta).

Se l’utente preme back, allora all’activity viene invocato il metodo onPause() forzandone la messa in pausa, (stato PAUSED) e quando sarà comparsa l’activity precedente, verrà invocato il metodo onClose() (stato STOPPED).

Cosa quasi simile accade se si presenta il secondo caso. Quando la nuova activity sta per essere visualizzata, viene immediatamente chiamato il metodo onPause() e a seconda se la nuova activity, una volta visualizzata, copra o meno tutto lo schermo, viene invocato il metodo onClose()

Nota: La differenza tra i due casi è solo che nel secondo c’è la possibilità che la nuova activity non copra tutto lo schermo e quindi non venga chiamato il metodo onClose(). Per il resto (e nel caso che la seconda activity copra tutto lo schermo) sono identici.

Caso 2: Nuova activity parzialmente visualizzata

Nel caso in cui l’activity parta (solito giro onCreate(), onStart() e onResume() ), si trovi nello stato RUNNING e venga visualizzata una seconda activity che però non ricopre completamente la prima activity (nel caso di una dialog o caso di activity con trasparenze), l’activity invoca il metodo onPause() e rimane in PAUSED.
Quando l’utente preme back (chiudendo la seconda activity), viene allora invocato il metodo onResume() e l’activity ritorna in RUNNING.

Case 3: Nuova activity a completo schermo

Nel caso in cui venga visualizzata una nuova activity mentre la nostra activity si trova nello stato RUNNING, viene invocato il metodo onPause() e si attende che la nuova activity venga visualizzata. Quando questa è completamente visibile (a tutto schermo) viene invocato, sulla nostra activity e non sulla nuova, il metodo onClose() mettendo l’activity in uno stato STOPPED.
Da questo momento in poi l’activity può essere rimossa se il sistema ha necessità di liberare memoria.

Supponiamo ora che l’utente dalla nuova activity preme back per tornare alla nostra activity che (come detto) si trova in STOPPED. Ci possono essere due casi:

Caso 3a: L’activity era ancora in memoria.

In questo caso android non la deve ricreare e quindi dallo stato STOPPED, invoca sull’activity il metodo onRestart() e successivamente il metodo onResume().

Caso 3b: L’activity era stata rimossa (deallocata)

In questo caso dallo stato stopped android (senza dirci nulla) ha liberato le risorse della nostra activity e l’ha uccisa (kill) mettendola di nuovo nello stato INACTIVE. Allora, siccome avendo premuto il tasto back quella activity deve essere visualizzata, il sistema ne crea una nuova e richiama nell’ordine i metodi onCreate(), onStart() e onResume().

Conservazione dello stato dell’activity

Fino ad ora abbiamo fatto ipotesi in cui le activity rimangono sempre in memoria e che il sistema non richieda la loro deallocazione. Tale ipotesi non è sempre vera e, come visto nel caso 3b, il sistema, quando necessita di risorse procede con l’eliminazione di tutte quelle activity INACTIVE prima e STOPPED poi.

Quindi immaginiamo il caso in cui la nostra activity stava in stato STOPPED, aveva delle variabili in mememoria e il sistema ce la “killa” sotto il naso. Quando viene premuto il tasto back e (come si aspettiamo) ritroviamo la nostra activity come l’abbiamo lasciata (questo per garantire la ‘user-experience’ anche in caso di low-memory) come ci si comporta? Abbiamo detto che android non può fare altro che ricrearne una nuova, quindi richiamare il metodi onCreate(), onStart() e onResume() ma le variabili che quella activity aveva in memoria al momento della sua “uccisione”?

Ecco a cosa servono quei due cilindretti rossi nello schema.

Anche se non fanno proprio parte del ciclo di vita di una activity, in prossimità di quei due cilindretti ci sono due metodi che vengono chiamati dal sistema per dare la possibilità di ripristinare uno stato precedentemente salvato e di salvare lo stato di un’activity prima della sua messa in pausa.

Il primo metodo che andiamo ad analizzare, invocato sempre prima del metodo onPause() è

[java]

public void onSaveInstanceState(Bundle bundle)

[/java]

che ci fornisce un Bundle dove possiamo salvare lo stato dell’activity un attimo prima di essere messa in pausa.

Da notare che a questo instante non sappiamo se poi la nostra activity sarà successivamente messa in CLOSED e poi in INACTIVE (e quindi distrutta) quindi è buona norma usare questo metodo per salvare tutti i dati dell’activity che vorremmo successivamente ripristinare.

Non c’è bisogno di salvare i dati all’interno delle VIEW (tipo il testo digitato all’interno degli EditText) che fanno parte dell’activity perché per quelle ci pensa direttamente android tramite altri meccanismi interni.

Il secondo metodo da analizzare (invocato dopo l’ onStart() e prima dell’ onResume()) è il metodo

[java]

public void onRestoreInstanceState(Bundle bundle)

[/java]

che riceve il bundle salvato in precedenza che ci consente di ripristinare i valori salvati. Successivamente viene invocato il metodo onResume() e di conseguenza possiamo far ripartire tutte le computazioni come se nulla fosse avvenuto.

Da notare che il Bundle ci viene fornito anche nel metodo onCreate(), quindi è una scelta applicativa (anche se ci sono delle regole che più o meno dovrebbero essere rispettate) per decidere dove ripristinare i dati.

Comments
To Top