Università degli Studi di Bologna Scuola di Ingegneria e Architettura Java e la piattaforma Android Corso di Laurea in Ingegneria Informatica Anno accademico 2012/2013 Prof. ENRICO DENTI Dipartimento di Informatica Scienza e Ingegneria (DISI) ANDROID Android è un sistema operativo per smartphone & tablet basato sul kernel di Linux che adotta Java come linguaggio di programmazione e incorpora quasi tutto il framework Java ma adotta nella propria infrastruttura la macchina virtuale Dalvik, diversa dalla JVM standard architettura a registri anziché a stack formato ottimizzato Dalvik Executable (.dex) anziché bytecode (.class)
ANDROID & JAVA Android è anche una piattaforma con i suoi strumenti di sviluppo: Android SDK di ambiente: plugin Eclipse (ADT) Cosa significa che "Android adotta Java"? stesso linguaggio: lo conoscete già! "quasi" stesse librerie (TRANNE la grafica.. comprensibilmente!) MA diversa macchina virtuale nell'infrastruttura, quindi diverso formato del codice oggetto e quindi diverso modello di sviluppo NON PIÙ bytecode (.class) MA Dalvik Executable (.dex) NON PIÙ archivi jar (.jar) MA application packages (.apk) NON PIÙ solo javac, MA ANCHE altri strumenti MA soprattutto diversa organizzazione delle applicazioni NON PIÙ main classico: le app sono organizzate in attività NON PIÙ console con stdout, stdin: le app sono solo grafiche STRUMENTI COME SI SVILUPPA in Android? con Eclipse purché prima si siano installati i necessari strumenti Android SDK ADT plugin per Eclipse e li si sia adeguatamente configurati (ahi ahi ) PASSI NECESSARI: installare una o più "Platforms" creare uno o più "Virtual Devices" configurare l'emulatore Android per tali dispositivi Sembra facile.. ma è piuttosto faticoso.. però alla fine funziona NB: l'emulatore è pesante e lento a partire.. ma poi è ottimo.
STRUMENTI: LA NOVITÀ Per ovviare alla difficoltà di configurazione dell'ambiente, da qualche mese è disponibile un ambiente preconfigurato un bundle unico che contiene tutto una versione speciale di Eclipse + tutti gli strumenti Naturalmente, rimane possibile configurare il proprio Eclipse a mano, per chi lo preferisce. Installazione manuale degli strumenti in Eclipse
PROCEDURA 1) Installazione dell'android SDK scaricare da http://developer.android.com/sdk/index.html scompattarlo e lanciare il setup configurarlo tramite l'sdk Manager (scelta e installazione piattaforme) 2) Installazione dell'adt Plugin per Eclipse DENTRO ECLIPSE: istruire Eclipse per lo scaricamento e installare il plugin aggiungere al plugin le informazioni sulla posizione dell'sdk 3) Configurazione dell'adt Plugin DENTRO ECLIPSE: riconoscimento piattaforme installate configurazione di uno o più dispositivi virtuali Android (AVD) 1. INSTALLAZIONE SDK Si può fare anche dopo, ma tanto vale farlo ora.
2. CONFIGURAZIONE SDK INDISPENSABILI: SDK Tools almeno una platform Android 4.0.3 (nuovi device) Android 2.3.3 (smartphone) oppure Android 3.x (dipende..) UTILI: USB driver (per scaricare app su dispositivi reali via cavo USB) 3. INSTALLAZIONE PLUGIN (1) DENTRO ECLIPSE: menu Help > Install New Software
3. INSTALLAZIONE PLUGIN (2) 3. INSTALLAZIONE PLUGIN (3)
3. INSTALLAZIONE PLUGIN (4) Normale, perché il plugin è ha bisogno di sapere la posizione dell'sdk. Fare quindi clic su Open Preferences (NON Close). 3. INSTALLAZIONE PLUGIN (5) Premere Browse e selezionare la cartella con l'sdk. Verificare che vengano trovate le piattaforme installate.
3. INSTALLAZIONE PLUGIN (5) Premere Browse e selezionare la cartella con l'sdk. Verificare che vengano trovate le piattaforme installate. 4. CONFIGURAZIONE PLUGIN (1) Dal menu Window, selezionare AVD Manager
4. CONFIGURAZIONE PLUGIN (2) Al momento, non ci sono dispositivi virtuali installati. Bisogna crearne almeno uno premendo New.. Definire un nome (in alto) Scegliere la piattaforma (es. 2.3.3) Scegliere la SD card size (1 GB) Scegliere la risoluzione (WVGA tipicam.) 4. CONFIGURAZIONE PLUGIN (3) Se ne volete altri, ripetete la procedura. Ora c'è almeno un dispositivo virtuale installato. La Console di Eclipse mostra il log della creazione avvenuta.
Creare applicazioni Android con Eclipse ANDROID & JAVA Sviluppare applicazioni per Android è molto simile a svilupparle per Java Standard sempre Eclipse, solito ambiente, soliti tool MA la diversa macchina virtuale (Dalvik) implica qualche passaggio in più PRIMA bastava generare il bytecode (.class) ORA, in più, occorre generare il Dalvik Executable (.dex) INOLTRE, per eseguirle occorre un emulatore emula un terminale Android con relativa GUI, tasti, etc possibile emulare vari dispositivi con diverse caratteristiche Soprattutto, è diversa l' organizzazione delle applicazioni NON HANNO PIÙ IL MAIN: sono organizzate in attività NON C'È PIÙ LA CONSOLE con stdout, stdin: sono grafiche
ANDROID: L'EMULATORE CONFIGURAZIONE DELL'EMULATORE L' Android Virtual Device manager gestisce i "finti terminali Android" da emulare Se ne possono definire molti, con diverse caratteristiche hw/sw - piattaforma (es. Android 2.3.3) - dimensione SD card size (es. 1 GB) - risoluzione (es. WVGA) - Per definirne altri, premere NEW e compilare la form
SVILUPPO DI APPLICAZIONI Per sviluppare un'applicazione, la prima cosa da fare è creare un apposito progetto Eclipse New > Android Application project APPLICAZIONI ANDROID (1) Perché una Applicazione Android ("app") non ha main? Perché c'è dietro un'idea diversa di scenario: un dispositivo smartphone/tablet è ben diverso da un pc! e, conseguentemente, di modello di runtime: le app devono essere fatte secondo un ben preciso schema perché così Android ne riconoscerà le parti costitutive e le integrerà nel suo framework d'esecuzione tale schema semplifica la realizzazione perché molti aspetti generali sono ereditati: SI SFRUTTA L'EREDITARIETÀ FIN DALLE FONDAMENTA un'app è fatta di una o più Activity ed è sempre descritta da un manifest (se non ha GUI, può essere un Service)
APPLICAZIONI ANDROID (2) E la grafica? Android NON HA Swing: fornisce la sua libreria i componenti grafici sono tutti sottoclassi di View sono sottoclassi di View anche text field, bottoni, etc strutturalmente, le View sono organizzate in gruppi un layout è quindi composto da View e da ViewGroups (invisibili) opportunamente annidati Soprattutto, a differenza di Java, le GUI non si programmano: si descrivono tramite un idoneo vocabolario XML ACTIVITY (1/4) L'attività (Activity) è il nucleo base di ogni applicazione una Activity "base" di per sé non fa nulla ma esiste! Attività vuota (di default) in esecuzione nell'emulatore
ACTIVITY (2/4) La classe Activity definisce vari metodi, che le singole attività possono (devono) specializzare il principale metodo è oncreate, chiamato quando l'attività inizia riceve un argomento Bundle, da usare per chiamare l'omonimo metodo padre via super (come Graphics in paintcomponent..) import android.app.activity; import android.os.bundle; public class CodFiscAndroidActivity extends Activity { @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); // il codice dell'attività ACTIVITY (3/4) Una semplice attività di prova che stampa HelloWorld può essere ottenuta semplicemente specializzando la view il metodo setcontentview specifica cosa mostrare a video lo "strano" argomento R.layout.main indica di visualizzare ciò che è specificato in un file XML esterno, main.xml di default, tale file specifica di mostrare un messaggio di saluto import android.app.activity; import android.os.bundle; public class CodFiscAndroidActivity extends Activity { @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main);
ACTIVITY (4/4) La prima Activity col messaggio di saluto predefinito: Nome della nostra Activity e messaggio di saluto (personalizzabile) Il Codice Fiscale su Android ESEMPIO: portare il codice fiscale su Android la logica dell'applicazione è immutata CodFisc resta com'è cambia l'interazione con l'utente: non più console, ma una GUI! Come si fa la nuova GUI? ci vuole una Activity, che specializzi oncreate e poi gestisca gli eventi, ad esempio il tasto DONE (Fatto) E come si programma una GUI in Android? non c'è Swing, ma la nuova libreria android.widget, con i suoi componenti e i suoi eventi un utile componente di testo è la TextView (non editabile) una sua sottoclasse editabile è EditView, a cui va associato l'apposito oneditoractionlistener che definisce il metodo oneditoraction
CodFiscAndroid ACTIVITY (1/2) import android.app.activity; import android.os.bundle; import android.widget.edittext; public class CodFiscAndroidActivity extends Activity { @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); EditText txt = new EditText(this); txt.setsingleline(); txt.settext("immettere i dati della persona: "); txt.setoneditoractionlistener(new MyListener(txt)); setcontentview(txt); Anziché il contenuto di default espresso da R.layout.main, si mostra il nostro componente. Ascoltatore degli eventi separato (per leggibilità) CodFiscAndroid ACTIVITY (2/2) import android.view.keyevent; import android.view.inputmethod.editorinfo; import android.widget.edittext; import android.widget.textview; public class MyListener implements TextView.OnEditorActionListener { private EditText output; public MyListener(EditText output){ this.output = output; public boolean oneditoraction(textview v, int actionid, KeyEvent event) { if (actionid!= EditorInfo.IME_ACTION_DONE) return false; String line = v.gettext().tostring(); String[] args = line.split("\\s+"); // separa args per calcolacodice String codice = CodFisc.calcolaCodice(args[1], args[0], Integer.parseInt(args[2]), Integer.parseInt(args[3]), Integer.parseInt(args[4]), args[5].equals("m")? CodFisc.MASCHIO : CodFisc.FEMMINA, args[6] ); output.settext(codice); return true; Per convenzione, si restituisce true se l'evento è stato gestito, false altrimenti La costante IME_ACTION_DONE rappresenta il tasto DONE (enter): se il tasto premuto non è quello, usciamo senza fare nulla.
LA NOSTRA APP EMULATA DEPLOYMENT DELL'APPLICAZIONE E per installarla su un vero telefono? o lo si collega via USB a Eclipse (tramite l'usb driver), spegnendo l'emulatore, così da usarlo al suo posto; oppure, più semplicemente, si copia l'apk manualmente Come si copia l'apk manualmente? l'apk (analogo del jar) è generata automaticamente da Eclipse basta copiarla nel telefono (ad esempio nella cartella download) dopo averlo collegato via USB
LA NOSTRA APP: INSTALLAZIONE doppio clic Screenshot catturati da Samsung Galaxy S I9000 LA NOSTRA APP: ESECUZIONE Screenshot catturati da Samsung Galaxy S I9000
Il linguaggio non cambia Java è sempre Java RIASSUMENDO Il framework applicativo è diverso diverso processo di sviluppo diversi strumenti diversa libreria grafica diverso modo di organizzare l'applicazione TUTTO QUI..? NULL'ALTRO DA SEGNALARE..? in realtà no : c'è molto altro..!..ovvero? GUI definite tramite risorse anziché programmaticamente altri concetti oltre all'activity (Intent.. e non solo) GUI TRAMITE RISORSE?? In Swing, le GUI vengono programmate scrivendo codice (eventualmente con l'aiuto di strumenti come WindowBuilder) In Android, fare le GUI in tal modo è possibile.. l'abbiamo appena fatto! ma non è ciò che si fa di solito, perché c'è una alternativa migliore: descrivere la GUI senza programmarla! per evitare che piccoli cambiamenti grafici comportino grandi (e faticosi) cambiamenti nel codice NUOVO APPROCCIO: il codice Java si limita a caricare un layout descritto altrove la descrizione del layout è fatta in un opportuno file XML esterno che specifica quali componenti grafici ci sono, dove sono, che dimensione hanno. e tutte le loro proprietà
IL NUOVO APPROCCIO L'activity NON definisce più i componenti setcontentview carica sempre e solo R.layout.main il suo vero (e unico) compito è agganciare i listener Il layout desiderato si definisce nel file main.xml Eclipse lo genera sempre automaticamente: in effetti c'era anche prima.. ma l'abbiamo volutamente ignorato! ORA, invece, lo apriamo e modifichiamo opportunamente! gli strumenti per farlo sono già presenti nell'android plugin con tanto di preview! RISULTATO: fare una GUI anche complessa è mooolto più semplice e non è finita: le risorse esterne comprendono anche stringhe e immagini, che quindi non è più necessario cablare nel codice! CodFiscAndroid REVISED import android.app.activity; import android.os.bundle; import android.widget.edittext; public class CodFiscAndroidActivity extends Activity { @Override public void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); EditText txt = findviewbyid(r.id.mytxt); txt.setoneditoractionlistener(new MyListener(txt)); Il listener ovviamente è identico Il contenuto è sempre espresso da R.layout.main, che però ora è personalizzato e specifica un EditText di nome mytxt Per recuperare un riferimento allo EditText di nome mytxt (necessario per agganciargli il listener), si usa la funzionefindviewbyid
CodFiscAndroid -main.xml <?xml version="1.0" encoding="utf-8"?> Header obbligatorio in tutti i file XML <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" Namespace obbligatorio per Android android:orientation="vertical" > Descrizione layout desiderato <EditText android:id="@+id/mytxt" android:singleline="true" android:layout_width="fill_parent" android:layout_height="wrap_content" android:hint="@string/immissione"> </EditText> </LinearLayout> La frase che appare nel campo di testo è specificata dalla stringa di nome immissione, a sua volta specificata nel file ausiliario strings.xml ELENCO COMPONENTI presenti nel layout Qui ce n'è uno solo, un EditText di nome mytxt Di seguito, le sue proprietà: singleline hint (la frase che appare) CodFiscAndroid -strings.xml La stringa di nome immissione ha come valore "Immettere i dati della persona:" Ma scrivere direttamente strings.xml è scomodo, quindi c'è un editor visuale già incluso!
CodFiscAndroid -strings.xml NOMI delle stringhe definite in strings.xml Cliccandoci sopra. La stringa di nome immissione ha come valore "Immettere i dati della persona:" LA NUOVA APP
CodFiscAndroid -main.xml In realtà, non è solo strings.xml ad avere un editor: anche main.xml ne ha uno, per non scrivere XML a mano! Preview Strumenti di zoom DUNQUE, non è necessario programmare la GUI a mano: si usa l'editor visuale! Palette con tutti i componenti disponibili L'ULTIMA CURIOSITÀ Nel codice così riorganizzato, c'era sempre quello "strano" riferimento a un argomento R.layout.main Chi è questo R? È il file delle RISORSE: generato automaticamente da Eclipse, contiene in forma Java le definizioni delle risorse definite nei vari file XML Più precisamente, contiene gli identificativi univoci di: identificatori (nomi di componenti grafici) layout (non è detto ci sia solo main ) stringhe immagini e icone
OLTRE LE ACTIVITY: Intent Per connettere più attività fra loro, Android introduce il concetto di Intent come descrizione astratta di una operazione da eseguire e può essere: esplicito, quando si specifica per nome l'attività da eseguire; implicito, quando si specifica solo l azione da eseguire (non l'attività esatta che dovrà eseguirla) Idea chiave: scrivere applicazioni i cui pezzi interagiscono fra di loro senza necessariamente doversi conoscere ESEMPIO 1 Applicazione articolata in due attività, una che mostra una lista di URL e un'altra che visualizza l'url scelto. l'intent creato dalla prima attività contiene il nome della seconda attività da attivare (ovvero, il nome esatto della classe visualizzatrice) ESEMPIO 2 Applicazione articolata in due attività, una che mostra una lista di URL e un'altra che invia l'url scelto. l'intent creato dalla prima attività contiene il nome della azione da svolgere (ACTION_SEND), non CHI la svolgerà sarà Android a scegliere la classe da attivare fra chi accetta quell'intent INTENT Un Intent è un'istanza di android.content.intent può essere definito programmaticamente o come risorsa è assimilabile a un "segnale" (evento) asincrono, che viene ricevuto da quei componenti (attività o servizi) registrati per esso può contenere dati, utili al ricevente per decidere cosa fare ESPLICITO: l'attività Pippo attiva l'attività Pluto Intent i = new Intent( this, Pluto.class); IMPLICITO l'attività Pippo attiva una attività capace di svolgere l'azione VIEW Intent i = new Intent( Intent.ACTION_VIEW, Uri.parse("http://...")); Se ci sono più componenti capaci di svolgere l'azione, apparirà un dialog per scegliere quale attivare.
ESEMPIO (1/10) Singolarmente presa, questa attività mostra a video una lista di voci fra cui scegliere ma per ora non fa nulla (non ci sono gestori di eventi) public class MyActivity extends ListActivity { protected void onresume() { super.onresume(); Estendiamo direttamente ListActivity (anziché genericamente Activity) setlistadapter(new ArrayAdapter<String>( this, android.r.layout.simple_list_item_1, android.r.id.text1, new String[] { "http://edenti.deis.unibo.it", "http://www.unibo.it", "http://www.google.it" ));... // da completare ESEMPIO (2/10) Aggiungiamo perciò un event listener, che: gestisca l'evento "voce scelta" attivando una URLActivity passandole un Intent ESPLICITO contenente, come dato extra, l'url da aprire (recuperato dalla lista) e infine attivando tale nuova attività public class MyActivity extends ListActivity {... // parte precedente protected void onlistitemclick(listview l, View v, int pos, long id) { Intent intent = new Intent(this, URLActivity.class); intent.putextra("url", (String) getlistadapter().getitem(pos)); startactivity(intent);... // da completare
ESEMPIO (3/10) A sua volta, la nuova attività, chiamata esplicitamente: recupera il dato rilevante (l'url da aprire) dall' Intent ricevuto e lo visualizza tramite un opportuno componente di sistema (una WebView) public class URLActivity extends Activity { protected void oncreate(bundle savedinstancestate){ super.oncreate(savedinstancestate); WebView webview = new WebView(this); setcontentview(webview); String url = getintent().getstringextra("url"); settitle(url); webview.loadurl(url); ESEMPIO (4/10) Infine permettiamo alla prima attività di reagire all'evento "long touch" sull'indirizzo selezionato a tale fine, aggiungiamo alla MyActivity un listener per l'evento ItemLongClick (in aggiunta al listener per ItemClick che c'era già) public class MyActivity extends ListActivity {... // parte precedente protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); getlistview().setonitemlongclicklistener(new MyLongClickListener( )); Due argomenti: l'attività sui cui agire (this) e il msg da mostrare nel chooser (String) In particolare, vogliamo che la reazione al "long touch" sia l'offerta di condividere il valore selezionato (qui, un URL) di conseguenza, MyLongClickListener dovrà costruire un apposito INTENT per attivare una attività "condividi elemento"
ESEMPIO (5/10) L'attività "condividi elemento" non la definiamo: c'è già! nei sistemi Android, "condividi con " è parte dei servizi standard (condividi via Bluetooth, via Facebook, etc) usiamo perciò un Intent IMPLICITO per avvisare tutte le attività esistenti, potenzialmente interessate a questo tipo di lavoro public class MyLongClickListener implements OnItemLongClickListener{ public boolean onitemlongclick(adapterview<?> adapterview, View view, int pos, long id) { final Intent intent = new Intent(Intent.ACTION_SEND); intent.settype("text/plain"); intent.putextra(intent.extra_text, (String) adapterview.getitematposition(pos)); mymainact.startactivity( Intent.createChooser(intent, mymsg))); return true;...// da completare Messaggio personalizzato da mostrare nel chooser Azione da svolgere (si cercano volontari ) Dati extra per l'intent Attivazione di un'ulteriore attività: un chooser per scegliere fra tutte le attività capaci di gestire l'action_send ESEMPIO (6/10) Ovviamente, c'è qualche dettaglio da sistemare: il listener agisce sull'activity principale (mymainact), che quindi deve essere ricevuta come parametro dal costruttore del listener idem per il messaggio personalizzato (mymsg) public class MyLongClickListener implements OnItemLongClickListener{ private String mymsg; private Activity mymainact; public MyLongClickListener(Activity mymainact, String mymsg) { this.mymsg = mymsg; this.mymainact = mymainact;...// tutto il resto public class MyActivity extends ListActivity { // nel metodo oncreate, la creazione del listener avverrà così: // new MyLongClickListener(this, getstring(r.string.my_msg))
ESEMPIO (7/10) Da ultimo, occorrono le giuste dichiarazioni nel manifest <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="it.unibo.deis.edenti" android:versioncode="1" android:versionname="1.0" > <uses-sdk android:minsdkversion="10" /> <uses-permission android:name="android.permission.internet"/> <application android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <activity android:name=".myactivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.main" /> <category android:name="android.intent.category.launcher" /> </intent-filter> </activity> <activity android:name=".urlactivity"/> </application> </manifest> e nelle risorse stringa: ESEMPIO (8/10) click semplice Qui non è apprso alcun chooser: evidentemente, questo emulatore conosce un'unica attività capace di gestire l'action_send
ESEMPIO (9/10) long touch Qui non è apparso alcun chooser: evidentemente, in questo emulatore è registrata un'unica attività per l'action_send, quella che effettua l'invio via mail. ESEMPIO (10/10) long touch È il titolo che abbiamo definito noi nelle risorse (my_msg) Su questo smartphone, invece, il chooser appare e offre la scelta fra parecchie applicazioni