Capire e programmare le Activity

25 Gennaio 200931 commenti

Cosa sono le activity? Non ve lo dico subito, :-) cerchiamo prima di capire come esse si collocano nella definizione di un'applicazione sotto Android. Le activity sono uno dei 4 elementi di base che vanno a costituire un'applicazione:
  • Activity
  • Broadcast Intent Receiver
  • Service
  • Content Provider
Un'activity puo' essere vista come la rappresentazione di una schermata (screen). La piattaforma di Android mantiene in primo piano (foreground) un solo screen alla volta, portando in secondo piano (background) gli altri. Lo stesso concetto si applica ai "processi  in foreground", ma questo tema sara' discusso nei prossimi tutorial. I vari screen (e, in generale, le varie Activity) sono "impilati" come un mazzo di carte: il tasto BACK permette di passare da uno screen all'altro, portando in background lo screen attuale e assegnado il foreground al precedente. Questa sorta di history delle Activity viene chiamata "activity stack"e viene gestito dall'Activity Manager, uno dei componenti del middelware della piattaforma (ricordo che, in generale, la piattaforma e' costituita da 3 strati: il sistema operativo Linux-like, il middelware, lo strato applicativo).


Per dovere di cronaca vorrei ricordare un fatto fondamentale (anche se spesso ignorato):

alla base di Android abbiamo Linux. In generale quando avvio un’applicazione sotto Linux, essa viene lanciata in un nuovo processo, ma cosa succede in un’applicazione di Android? In generale l’intera applicazione gira all’interno di un unico processo di sistema, quindi ogni Activity condivide quest’ultimo!

Per una descrizione estensiva (e complicata) vi rimando al questo link.

Diamo ora una breve occhiata al life cycle di un’Activity, rappresentato nell’immagine seguente:

Activity life cycle

I metodi elencati nell’immagine sono gestiti da Android stesso, e possono subire un override per permettere allo sviluppatore di decidere quali azioni intrapendere a seguito di un particolare stato.

Ecco una breve spiegazione dei metodi:

  • onCreate(Bundle): e’ richiamato quando l’activity viene avviata per la prima volta.Il metodo accetta un parametro che puo’ essere nullo, oppure puo’ ritornare le informazioni salvate precedentemente con il metodo onSaveInstanceState( )
  • onStart( ):indica che l’activity sta per essere visualizzata
  • onResume( ): richiamato quando l’activity comincia ad “interagire” con l’utente: e’ qui che conviene gestire elementi multimediali (musica, ecc).
  • onPause( ): viene richiamato quando l’activity sta per andare in background, normalmente perche’ e’ stata avviata un’altra activity che si prepara a prendere il foreground. In questo metodo conviene SALVARE tutti i dati persistenti (eg: se l’activity ha lo scopo di editare un SMS, salvare lo stato attuale, le modifiche apportate dall’utente, per poi ripristinarle sull’onResume)
  • onStop( ): richiamato quando l’activity non e’ piu’ visibile all’utente.Se la memoria e’ poca, questo metodo NON verra’ invocato e Android killera’ il processo associato a questa applicazione (NB: l’applicazione puo’ sopravvivere anche senza il suo processo)
  • onRestart( ): se e’ richiamato, significa che l’activity sta per essere ridisegnata
  • onDestroy( ): richiamato giusto prima che l’activity venga distrutta.Se la memoria e’ poca, il metodo NON verra’ richiamato e Android killera’ il processo associato all’applicazione.
  • onSaveInstanceState(Bundle): Android invoca questo metodo per salvare alcune informazioni di stato dell’activity, come la posizione del cursore all’interno di un text field. Normalmente non serve eseguire l’override di questo metodo, perche’ Android salva per noi queste informazioni di stato aggiuntive
  • onRestoreInstanceState(Bundle): richiamato solo se alcuni stati dell’activity sono precedentemente stati salvati con onSaveInstanceState
support-tips-icon Un metodo veloce per testare il life cycle di un’activity consiste nel cambiare l’orientazione del display (eg da landscape a prtrait..Ctrl+F11 sull’emulatore):in questo modo Android naviga di disgramma di stato visto sopra con l’intento di distruggere l’activity, per poi rilanciarla..

Ok, ho la mia bella Activity presa dallo stack ed ha ottenuto il foreground:

perfetto, ma come faccio a visualizzare qualcosa sul display?

La risposta e’: tramite le viste, chiamate View.

Non e’ obiettivo di questo tutorial spiegare in modo approfondito cosa siano le View, ma e’ giusto dare qualche accenno.

Una View e’ la classe di base per la definizione dell’interfaccia grafica e la gestione degli eventi nati dall’interazione dell’utente con la UI, del focus, ecc.

L’interfaccia grafica di un’Activity viene in genere definita da una o piu’ View, organizzate in una struttura ad albero e rappresentate sullo schermo tramite il Layout (group view), come spiegato qui.

viewgroup

Esistono vari tipi di layout che permettono allo sviluppatore grandi libertà implementative: e’ inoltre sempre possibile definire un proprio Layout, estendendo quelli di default.

E’ importante sapere che una View (e layout) può essere definita completamente da codice oppure tramite un file XML: nel primo caso parliamo di programmazione procedurale, nel secondo caso di programmazione dichiarativa.

In generale è consigliato l’utilizzo dei file XML (e non solo per la definizione delle UI), in quanto meno complicato (almeno dovrebbe esserlo ;-) ) e facilmente modificabile. Android fornisce il supporto per entrambe le tipologie di programmazione.

Come vedremo in seguito, dopo aver definito la mia interfaccia grafica in un file XML (main.xml, inserito nella directory /res/layout/), posso caricarla nell’activity col comando:

[java]setContentView(R.layout.main);[/java]

Bene, passiamo a qualche esempio?

NO! :-)

Manca ancora un’informaizone fondamentale: come faccio a passare da un’activity ad un’altra, oppure, in generale, come avvio una determinata activity?

La risposta e’: tramite gli Intent. Un Intent rappresenta un meccanismo per descrivere la richiesta di “un’azione”, come ad esempio:

  • voglio aprire il link http://www.google.it nel browser;
  • voglio scattare una foto;
  • vaglio avviare l’applicazione “pinco pallino”;

Questa e’ decisamente una delle novita’ di Android: quando lo sviluppatore crea una nuova applicazione, egli definisce cosa e’ in grado di fare quest’ultima (cio’ viene realizzato tramite il concetto di IntentFilter).

Questo meccanismo offre 2 notevoli vantaggi:

  • Le activity possono riutilizzare (ma che bel concetto :-) ) funzionalita’ offerte da altri componenti, semplicemente costruendo un corretto Intent e inserendo in quest’ultimo le informazioni necessarie a soddisfare la richiesta.
  • Un’activity puo’ essere COMPLETAMENTE sostituita da un’altra, definendo gli stessi IntentFilter.

Il meccanismo col quale android avvia un’activity in base ad un IntentFilter non e’ triviale e viene riportato qui. Diciamo inoltre che un Intent puo’ essere avviato senza aspettarsi alcun risultato, oppure lo si puo’ avviare richiedendo il ritorno di un risultato.

Vediamo brevemente alcune metodologie per creare e lanciare un Intent.

In questo primo esempio creo un progetto (basato su HelloWorld), modificando il layout di default main.xml, aggiungendo un pulsante, associando a quest’ultimo un listener per l’evento “click” e facendo partire una seconda Activity tramite (SecondaActivity.java) un semplice Intent.

Prima di tutto modifichiamo il layout main.xml aggiungendo il pulsante:

tutorial1-mainxml

il codice XML risultante e’ il seguente:

tutorial1-main2xml

Diamo ora una veloce occhiata al codice dell’activity principale (ActivityTest.java):

[java]package com.android.activitytest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class ActivityTest extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button bnt = (Button)findViewById(R.id.bntStartSecondActivity);
bnt.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
// avvio la seconda activity
}
});
}
}[/java]

Vi faccio notare che la classe ActivityTest deriva da android.app.Activity, classe di base per la creazione di un’activity; esistono altre tipologie di activity gia’ “pronte”, come l’importante ListActivity e MapActivity ecc..

Si dia un’occhiata all’immagine seguente, che mostra la gerarchia di Activity:

tutorial1-activitygerarchiaxml

Come gia’ spiegato precedentemente, il metodo setContentView mi permette di “caricare” la vista all’interno dell’activity.

Il metodo findViewById recupera una View (in questo caso un Button) partendo dall’ID che lo identifica.

Il discorso qui si farebbe veramente lungo, ma diciamo che quando definisco un XML (programmazione dichiarativa), ed assegno un ID ad una vista (eg android:id=”@+id/bntStartSecondActivity..NB: il carattere ‘+’ richiede ad android di creare questo nuovo ID!), android crea AUTOMATICAMENTE una variabile all’interno del file R.java che identifica la risorsa stessa e permette di recuperarla da codice (programmazione procedurale). Nel nostro caso specifico e’ stato creata la variabile intera bntStartSecondActivity.

Ok, spero di non avervi fatto troppa confusione :-). Le righe successive definiscono un nuovo listener in ascolto sull’evento “click”..a seguito di questo evento andremo a lanciare l’Intent, con l’intento :p di avviare una nuova activity. Creiamo prima di tutto una nuova activity (SecondActivity.java) e associamo ad essa il layout second.xml, come mostrato di seguito:

tutorial1-secondxml

OK, ora andiamo ad invocarla (sempre nella definizione del listener)! Il modo piu’ semplice consiste nel creare un Intent con il nome della classe, in questo caso SecondActivity:

[java]public void onClick(View view) {
// avvio la seconda activity
Intent i = new Intent(view.getContext(), SecondActivity.class);
view.getContext().startActivity(i);
}[/java]

Bene, provate ad avviare l’applicazione lanciando il debug sulla classe principale ActivityTest.java..premete il pulsante e…NON FUNZIONA !! :-)

Perche’ ?

Beh perche’ ho chiesto ad Android di avviare un’activity (tramite l’intent) che NON CONOSCE. L’activity va infatti dichiarata all’interno del manifest, altrimenti essa e’ ignorata!!!

tutorial1-missing-activity

Dichiarando l’activity nel manifest vedrete che il pulsante vi fara’ passare dalla prima alla seconda Activity, mentre il tasto di Back vi fara’ tornare alla prima delle due.

Ora facciamo questa semplice prova: aggiungiamo al layout della prima activity (main.xml) 2 campi dove inseriremo dati numerici che andremo a passare alla seconda activity richiedendo un risultato; la seconda activity ( nel nostro esempio didattico ) calcolera’ la somma tramite un nuovo pulsante “calcola”, che terminera’ l’activity stessa (tramite il metodo finish) e ritornera’ il risultato alla prima activity. Allora prima di tutto modifichiamo main.xml e second.xml come segue:

tutorial1-3mainxml

tutorial1-second2xml

Ecco quello che succede quando avvio l’applicazione:

calcola1

calcola2

calcola3

I sorgenti della prima e della seconda activity sono riportati di seguito:

[java]package com.android.activitytest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class ActivityTest extends Activity implements OnClickListener{
public static String VALORE_1 = “VALORE_1”;
public static String VALORE_2 = “VALORE_2”;
public static final int SOMMA = 10;
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button bnt = (Button)findViewById(R.id.bntStartSecondActivity);
bnt.setOnClickListener(this);
}

@Override
public void onClick(View v) {
// creo l’intent per richiamare la seconda activity
Intent i = new Intent(this, SecondActivity.class);
// recupro i valori impostati nei 2 field
EditText etxt1 = (EditText)findViewById(R.id.EditText01);
EditText etxt2 = (EditText)findViewById(R.id.EditText02);
String val1str = etxt1.getText().toString();
String val2str = etxt2.getText().toString();
int val1 = Integer.parseInt(val1str);
int val2 = Integer.parseInt(val2str);
i.putExtra(VALORE_1, val1);
i.putExtra(VALORE_2, val2);
// avvio la seconda activity
startActivityForResult(i, SOMMA);
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
int risultato = extras.getInt(SecondActivity.RISULTATO);
if (requestCode == SOMMA) {
TextView t = (TextView)this.findViewById(R.id.TextView03);
t.setText(“risultato = “+ risultato);
}
}
}
}[/java]

[java]package com.android.activitytest;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class SecondActivity extends Activity implements OnClickListener{
public static String RISULTATO = “RISULTATO”;
private int val1;
private int val2;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.second);
Button bnt = (Button)findViewById(R.id.bntCalcola);
bnt.setOnClickListener(this);
TextView t = (TextView)findViewById(R.id.TextView01);
// recupero i valori passati dalla prima Activity
Bundle extras = getIntent().getExtras();
val1 = extras.getInt(ActivityTest.VALORE_1);
val2 = extras.getInt(ActivityTest.VALORE_2);
t.setText(“Ho ricevuto i valori “+val1+” e “+val2);
}

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
// calcolo il risultato
int risultato = val1 + val2;
// ritorno il risultato alla activity chiamante:
// memorizzo il risultato in un bundle
Bundle bundle = new Bundle();
bundle.putInt(RISULTATO, risultato);
// costruisco l’intent di ritorno
Intent mIntent = new Intent();
// inserisco il Bundle
mIntent.putExtras(bundle);
// ritorno il risultato
setResult(RESULT_OK, mIntent);
// chiudo questa activity
finish();
}

}[/java]

Ecco alcune note e informazioni:

  • come e’ facile vedere, ho fatto in modo che le 2 activity implementino OnClickListen: in questo modo accedo in modo semplice a variabili locali..
  • utilizzo il metodo startActivityForResult che richiede un risultato da parte della activity avviata a seguito del lancio dell’intent; tale activity notifica il risultato utilizzando il metodo setResult
  • e’ importante che nel metodo onActivityResult venga eseguito il controllo del codice di ritorno (nel nostro caso tutto va a buon fine e viene ritornato RESULT_OK)

Spero che il codice sia sufficientemente autoesplicativo, comunque fate degli esperimenti, e’ il modo migliore per imparare!

Un’ultima parentesi (che sarebbe lunga..); negli esempi riportati sin’ora l’Intent e’ stato costruito passando direttamente la classe della seconda activity da caricare. Invece di far questo posso utilizzare gli IntentFilter per dichiarare “cosa” la seconda Activity e’ in grado di fare e posso costruire un Intent che sia adatto a tale IntentFilter; in breve:

[java]@Override
public void onClick(View v) {
// creo l’intent per richiamare la seconda activity
//Intent i = new Intent(this, SecondActivity.class);
Intent i = new Intent();
i.setAction(SecondActivity.CALCOLA_ACTION);
}[/java]

La seconda activity definisce l’azione CALCOLA_ACTION

[java]
public static final String CALCOLA_ACTION = “com.android.activitytest.CALCOLA_ACTION”;
[/java]

ed infine modifico il manifest aggiungendo l’intent filter:

tutorial1-intentfilter

 

Grazie a tutti per la pazienza :-)

Loading...
Social Media Auto Publish Powered By : XYZScripts.com