Docsity
Docsity

Prepara i tuoi esami
Prepara i tuoi esami

Studia grazie alle numerose risorse presenti su Docsity


Ottieni i punti per scaricare
Ottieni i punti per scaricare

Guadagna punti aiutando altri studenti oppure acquistali con un piano Premium


Guide e consigli
Guide e consigli


Programmazione Android, Appunti di Programmazione Java

Appunti di programmazione android

Tipologia: Appunti

2016/2017

Caricato il 07/11/2017

fabio.moscariello
fabio.moscariello 🇮🇹

4

(1)

3 documenti

1 / 26

Toggle sidebar

Questa pagina non è visibile nell’anteprima

Non perderti parti importanti!

bg1
di 126
Mobile Programming (BDSIR)
Le fondamenta di Android
Un’Activity è un’interfaccia utente. Ogni volta che si usa un’app generalmente si interagisce con
una o più “pagine” mediante le quali si consultano dati o si immettono input.
Un Service svolge un ruolo, se vogliamo, opposto allActivity. Rappresenta un lavoro,
generalemente lungo e continuato, che viene svolto interamente in background senza bisogno di
interazione diretta con l’utente. I Service spesso preparano i dati che le activity devono mostrare
all’utente permettendo una reattività maggiore nel momento della visualizzazione.
Un Content Provider nasce con lo scopo della condivisione di dati tra applicazioni. La sua finalità
richiama quel principio di sicurezza dell’applicazione il cui spazio di memoria non deve essere
invaso da un’altra applicazione. Questi componenti permettono di condividere, nell’ambito del
sistema, contenuti custoditi in un database, su file o reperibili mediante accessi in Rete. Tali
contenuti potranno essere usati da altre applicazioni senza invadere lo spazio di memoria.
Un Broadcast Receiver è un componente che reagisce ad un invio di messaggi a livello di
sistema – appunto in broadcast – con cui Android notifica l’avvenimento di un determinato evento,
ad esempio l’arrivo di un SMS o di una chiamata o sollecita l’esecuzione di azioni. Questi
componenti come si può immaginare sono particolarmente utili per la gestione istantanea di
determinate circostanze speciali.
Molto importante ricordare che una componente può attivarne un’altra mediante apposite
invocazioni di sistema. Questa intenzione viene codificata con un Intent utilizzabile come normale
classe Java ma che sottintende un potentissimo strumento di comunicazione di Android.
Il ciclo di vita di un’app Android
Android sa che il fattore fondamentale della sopravvivenza di un sistema mobile è la corretta
gestione delle risorse.
Android farà in modo di tenere in vita ogni processo il più a lungo possibile. Ciò non toglie che
in alcune circostanze ed in base alle risorse hardware a disposizione, il sistema operativo si
troverà nella necessità di dover liberare memoria abbattendo processi.
Le applicazioni candidate all’eliminazione, vengono classificate per quanto siano importanti per la
user experience. Maggiore sarà l’importanza riconosciuta ad un’applicazione, minori saranno le
probabilità che venga arrestata.
Così facendo Android tenterà di raggiungere il suo duplice scopo: preservare il sistema e
salvaguardare l’utente.
1. Processi in “foreground”: sono quelli che" interagiscono direttamente o indirettamente con
l’utente. Stiamo parlando delle applicazioni che, ad esempio, "contengono l’Activity attualmente
utilizzata o i Service ad essa collegati. Questi sono i processi che Android tenterà di preservare
maggiormente. Importante notare che anche le applicazioni in foreground non sono del tutto al
sicuro. Se ad esempio il sistema non disponesse di risorse sufficienti a mantenerli tutti in vita, si
troverebbe costretto ad arrestarne qualcuno;
2. Processi visibili: hanno una priorità inferiore a quelli in foreground ma vengono anch’essi
grandemente tutelati da Android. Infatti, avendo componenti ancora visibili all’utente anche se non
vi interagiscono più, svolgono comunque un ruolo particolarmente critico. Anche in questo caso si
tratta di Activity visibili e Service ad esse collegati;
3. Processi “service”: contengono dei service in esecuzione che generalmente svolgono lavori
molto utili all’utente anche se non direttamente collegati con ciò che egli vede nel display. Il loro
livello di priorità può essere considerato medio;
4. Processi in “background”: contengono activity non più visibili all’utente. Questa è una
categoria solitamente molto affollata composta dal gran numero di applicazioni che l’utente ha
usato e messo poi in disparte, ad esempio premendo il tasto Home. Non sono considerati molto
importanti e sono dei buoni candidati all’eliminazione in caso di scarsità di risorse;
5. Processi “empty”: sono praticamente vuoti nel senso che non hanno alcuna componente di
sistema attiva. Vengono conservati solo per motivi di cache, per velocizzare la loro riattivazione
pf3
pf4
pf5
pf8
pf9
pfa
pfd
pfe
pff
pf12
pf13
pf14
pf15
pf16
pf17
pf18
pf19
pf1a

Anteprima parziale del testo

Scarica Programmazione Android e più Appunti in PDF di Programmazione Java solo su Docsity!

Mobile Programming (BDSIR)

Le fondamenta di Android

Un’ Activity è un’interfaccia utente. Ogni volta che si usa un’app generalmente si interagisce con una o più “pagine” mediante le quali si consultano dati o si immettono input. Un Service svolge un ruolo, se vogliamo, opposto all’Activity. Rappresenta un lavoro, generalemente lungo e continuato, che viene svolto interamente in background senza bisogno di interazione diretta con l’utente. I Service spesso preparano i dati che le activity devono mostrare all’utente permettendo una reattività maggiore nel momento della visualizzazione. Un Content Provider nasce con lo scopo della condivisione di dati tra applicazioni. La sua finalità richiama quel principio di sicurezza dell’applicazione il cui spazio di memoria non deve essere invaso da un’altra applicazione. Questi componenti permettono di condividere, nell’ambito del sistema, contenuti custoditi in un database, su file o reperibili mediante accessi in Rete. Tali contenuti potranno essere usati da altre applicazioni senza invadere lo spazio di memoria. Un Broadcast Receiver è un componente che reagisce ad un invio di messaggi a livello di sistema – appunto in broadcast – con cui Android notifica l’avvenimento di un determinato evento, ad esempio l’arrivo di un SMS o di una chiamata o sollecita l’esecuzione di azioni. Questi componenti come si può immaginare sono particolarmente utili per la gestione istantanea di determinate circostanze speciali. Molto importante ricordare che una componente può attivarne un’altra mediante apposite invocazioni di sistema. Questa intenzione viene codificata con un Intent utilizzabile come normale classe Java ma che sottintende un potentissimo strumento di comunicazione di Android.

Il ciclo di vita di un’app Android

Android sa che il fattore fondamentale della sopravvivenza di un sistema mobile è la corretta gestione delle risorse. Android farà in modo di tenere in vita ogni processo il più a lungo possibile. Ciò non toglie che in alcune circostanze ed in base alle risorse hardware a disposizione, il sistema operativo si troverà nella necessità di dover liberare memoria abbattendo processi. Le applicazioni candidate all’eliminazione, vengono classificate per quanto siano importanti per la user experience. Maggiore sarà l’importanza riconosciuta ad un’applicazione, minori saranno le probabilità che venga arrestata. Così facendo Android tenterà di raggiungere il suo duplice scopo: preservare il sistema e salvaguardare l’utente.

1. Processi in “foreground” : sono quelli che interagiscono direttamente o indirettamente con l’utente. Stiamo parlando delle applicazioni che, ad esempio, contengono l’Activity attualmente utilizzata o i Service ad essa collegati. Questi sono i processi che Android tenterà di preservare maggiormente. Importante notare che anche le applicazioni in foreground non sono del tutto al sicuro. Se ad esempio il sistema non disponesse di risorse sufficienti a mantenerli tutti in vita, si troverebbe costretto ad arrestarne qualcuno; 2. Processi visibili : hanno una priorità inferiore a quelli in foreground ma vengono anch’essi grandemente tutelati da Android. Infatti, avendo componenti ancora visibili all’utente anche se non vi interagiscono più, svolgono comunque un ruolo particolarmente critico. Anche in questo caso si tratta di Activity visibili e Service ad esse collegati; 3. Processi “service” : contengono dei service in esecuzione che generalmente svolgono lavori molto utili all’utente anche se non direttamente collegati con ciò che egli vede nel display. Il loro livello di priorità può essere considerato medio; 4. Processi in “background” : contengono activity non più visibili all’utente. Questa è una categoria solitamente molto affollata composta dal gran numero di applicazioni che l’utente ha usato e messo poi in disparte, ad esempio premendo il tasto Home. Non sono considerati molto importanti e sono dei buoni candidati all’eliminazione in caso di scarsità di risorse; 5. Processi “empty” : sono praticamente vuoti nel senso che non hanno alcuna componente di sistema attiva. Vengono conservati solo per motivi di cache, per velocizzare la loro riattivazione

qualora si rendesse necessaria. Come ovvio, sono i candidati “numero 1” all’eliminazione da parte del sistema operativo.

Progetto di un’applicazione Android (eclipse)

L’ architettura di progetto è costituita da un certo numero di file e cartelle. Tutti sono importanti ma gli elementi tra i quali il programmatore dovrà sapere sono:

  • (^) la cartella src che conterrà tutto il codice Java che scriveremo;
  • (^) gen che conterrà file generati automaticamente, tra i quali il più importante R.java;
  • (^) assets che conterrà assets ad esempio assets/fonts/... per i quali NON vengono generati ID
  • (^) bin che conterrà file da istallare sul target device
  • (^) libs che conterrà librerie esterne a Java e al framework Android;
  • (^) la cartella res in cui risiederanno le cosiddette risorse dell’applicazione per la maggior parte configurate in XML ma non solo (file di layout, immagini, suoni, ecc.), essi vengono identificati con ID;
  • (^) il file AndroidManifest.xml anch’esso in XML che custodirà configurazioni e ruoli dei componenti della nostra app (permessi, attività, icona…);
  • (^) proguard-project.text per il tool ProGuard: compatta, ottimizza e offusca il codice sorgente (protezione);
  • (^) project.properties : specifica alcune proprietà dell’app.

Progetto di un’applicazione Android (AndroidStudio)

Il progetto è contenuto in una cartella denominata app. Questo è il modulo di default. L’IDE, infatti, suddivide un progetto in più moduli , ognuno dei quali può svolgere un ruolo diverso (libreria Java, libreria Android, inclusione di un progetto esterno, ecc). Il modulo app include:

  • (^) la cartella Java con il codice Java;
  • (^) la cartella res (contenente risorse per lo più realizzate in XML);
  • (^) un file di configurazione denominato AndroidManifest.xml. Dopo il modulo troviamo la sezione Gradle Scripts. Qui ci sono i file di build che userà Gradle per trasformare il nostro progetto in un’app funzionante. In particolare, i file di build sono due: uno per tutto il progetto ed uno per il solo modulo app. Un aspetto da non dimenticare è che, non appena apportata una modifica al file di build, si deve selezionare il pulsante Sync Project with Gradle files.

Esecuzione di un’app Android

Per avviare un’app Android abbiamo bisogno di un device, abbiamo due alternative: 1: Emulatore Android (Android Virtual Device)

  • Lento (a volte molto), alcune operazioni sono difficoltose
  • Ha dei bug
  • E’ un “simulatore”
  • E’ facile creare situazioni particolari (es. batteria scarica, arrivo di un messaggio, …) 2: Device reale
  • Veloce, l’input è facile da gestire (es. rotazione del display)
  • L’esecuzione è reale
  • Non è possibile creare situazioni particolari quando si vuole, ad esempio per avere la batteria scarica bisogna aspettare che si scarichi. L’IDE fornisce uno strumento molto utile per trovare errori (debug) nei programmi, il Debugger. Esso ci da la possibilità di eseguire l’applicazione step-by-step mediante l’uso di breakpoints, in tal modo possiamo controllare il valore delle variabili nei punti che vogliamo. Inoltre possiamo usare la classe Log che mediante il metodo statico d(String, String) ci permette di visualizzare dei messaggi che inseriamo all’interno del codice dell’app.

Layouts

Un’Activity ha sempre bisogno di un suo aspetto grafico, anche nei casi più semplici, come quando si limita a stampare la stringa “Hello World!”. La struttura grafica di un’Activity prende il nome di Layout. Un layout definisce l’aspetto grafico di un’interfaccia utente. Le interfacce utente possono essere create in due modi:

View e ViewGroup

Una classe che incontreremo spesso nelle interfacce utente Android è View: un qualunque elemento che appare in un’interfaccia utente e che svolge due funzionalità :

  • (^) mostra un aspetto grafico;
  • (^) gestisce eventi relativi all’interazione con l’utente. Una View è un rettangolo e la sua posizione nell’interfaccia è definita dalle coordinate dell’angolo in alto a sinistra. Una classe derivata da View è ViewGroup. Esso è sia un tipo di View sia un contenitore di altre View (es. ListView, GridView, ecc..). Le tipologie di View si articolano per lo più in tre categorie:
  • (^) i layout ;
  • (^) i widget che in Android rappresentano i controlli form sono delle View;
  • (^) gli AdapterView sono delle View, generalmente nello stesso package dei widget, che collaborano nella realizzazione del pattern Adapter. Le View come tutti gli elementi che popolano le interfacce android hanno un id , anch’esse infatti sono identificate nel file delle risorse mediante l’id. Gli attributi id hanno un valore definito come @+id/ identificatore dove per identificatore si intende il nome dell’id scelto dall’utente. Il simbolo + apposto dopo la @ indica che se l’id con quel nome non è stato ancora definito nel sistema sarà definito per l’occasione. Si potrà far riferimento ad un elemento tramite id in due modi:
  • (^) in XML con @id/nome, ad esempio in un attributo del RelativeLayout;
  • (^) nel codice Java come R.id.nome (es. Button pulsante = (Button) findViewById(R.id.pulsante); ).

Unità di misura in Android (px, dpi, dp)

Screen size : grandezza reale dello schermo (es. 4”) Screen density (dpi): quanti px ci sono nell’unità di area (pollice). Le densità sono raggruppate in LOW, MEDIUM, HIGH (240dpi), XHIGH (320dpi), XXHIGH (480dpi) e XXXHIGH (640 dpi) Una buona app dovrebbe fornire alternative per i drawable (oggetti da disegnare), una per ogni categoria di densità. Quantità di px reali del display : con una densità di 240dpi e uno schermo di 4” abbiamo: 240 x 4 = 960 px Density indipendent pixel (dp): la dimensione del dp è calcolata su una densità di 160dpi, un dp ha le dimensioni di un px a 160dpi. In tal modo la dimensione non dipenderà dalla densità reale.

Activity

Per creare un’Activity è necessario fare due cose:

  • (^) estendere la classe Activity , appartenente al framework Android;
  • (^) registrare l’Activity nell’AndroidManifest.xml mediante l’uso dell’apposito tag XML . Il codice Java che realizza l’Activity risiede nella cartella src. La classe si chiama MainActivity ed estende Activity. Al suo interno viene implementato l’override del metodo onCreate. A proposito delle due righe di codice presenti all’interno dell’onCreate:
  • (^) super.onCreate(savedInstanceState) : invoca il metodo omonimo della classe base. Questa operazione è assolutamente obbligatoria;
  • (^) setContentView(R.layout.activity_main) : specifica quale sarà il “volto” dell’Activity, il suo layout. Il suo effetto è quello di imporre come struttura grafica dell’Activity il contenuto del file activity_main.xml presente nella cartella res/layout.

Ciclo di vita di un’activity

Quando un’activity va in esecuzione per interagire direttamente con l’utente vengono obbligatoriamente invocati tre metodi:

  • (^) onCreate : l’activity viene creata. Il programmatore deve assegnare le configurazioni di base e definire quale sarà il layout dell’interfaccia;
  • (^) onStart : l’activity diventa visibile. È il momento in cui si possono attivare funzionalità e servizi che devono offrire informazioni all’utente;
  • (^) onResume : l’activity diventa la destinataria di tutti gli input dell’utente.

Android pone a riposo l’activity nel momento in cui l’utente sposta la sua attenzione su un’altra attività del sistema, ad esempio apre un’applicazione diversa, riceve una telefonata o semplicemente – anche nell’ambito della stessa applicazione – viene attivata un’altra Activity. Anche questo percorso, passa per due metodi di callback:

  • (^) onPause (l’inverso di onResume) notifica la cessata interazione dell’utente con l’activity;
  • (^) onStop (contraltare di onStart) segna la fine della visibilità dell’activity;
  • (^) onDestroy (contrapposto a onCreate) segna la distruzione dell’activity. Quando l’utente ruota lo schermo l’attività viene prima eliminata: onPause() => onStop() => onDestroy() - si perde lo stato dell’activity ; poi viene ricreata: onCreate() => onStart() => onResume() Quando l’utente preme il tasto Home: onPause() => onStop() quando ritorna all’activity: onRestart() => onStart() => onResume() Per non perdere lo stato dell’activity dopo quando viene chiamata onPause() viene chiamata anche la funzione onSaveInstanceState() in cui salviamo lo stato Che poi recuperiamo in onCreate() App esempio “ActivityLifeCycle”

Intent

Un’app normalmente è composta da più activity e ognuna di essa ha un compito specifico (modularità), ad esempio un’app che gestisce la posta elettronica è composta da:

  • (^) un’activity per la scrittura di un messaggio
  • (^) un’activity per la lettura di un messaggio
  • (^) ecc… Per questo motivo un’activity può richiedere l’esecuzione di un’azione da parte di un’altra activity mediante la classe Intent, una descrizione (astratta) di un’operazione da svolgere. Sono uno
  • (^) continuando a premere il tasto back si ritorna all’home screen. Quando un’activity viene portata in background va in stop() ma il suo backstack rimane intatto. Dato che un’activity può essere lanciata da più activity si possono avere istanze multiple nel backstack. App esempio “VisualizzaMappa”

Permessi

Android protegge risorse e dati con un meccanismo di permessi di accesso che servono a limitare l’accesso a:

  • (^) info dell’utente (es. contatti)
  • (^) servizi con costi (es. invio di SMS, accesso a Internet…)
  • (^) risorse di sistema (es. fotocamera, GPS…) I permessi di accesso vengono rappresentati da stringhe. Ogni app deve dichiarare nel manifest i “permessi” che intende utilizzare, l’utente li dovrà accettare al momento dell’installazione.

Threads

Solitamente un qualunque software vede contemporaneamente attivi più thread. Ognuno di essi svolgerà in maniera a sé stante una sequenza di operazioni, come se fosse “un programma nel programma”. Si consideri che quando si utilizza un’Activity il thread principale dell’applicazione si occupa prevalentemente di gestire i messaggi relativi al funzionamento dell’interfaccia utente. Svolgere in questo stesso thread operazioni presumibilmente “lente” come ad esempio la lettura e scrittura da file, il prelevamento di dati da un database, il caricamento di immagini rischierebbe di rendere poco reattiva la UI. Conseguenza di ciò sarebbe una user exprerience non troppo gradevole che porterebbe l’utente a sostituire la nostra app con altre molto più scattanti. Operazioni “lente” dovrebbero essere preferibilmente svolte su thread secondari, detti anche worker thread. Inoltre l’accesso in Rete, importantissimo nella programmazione moderna ma contraddistinto da tempi di latenza variabili, deve obbligatoriamente essere eseguito su un thread secondario. Ogni thread in esecuzione sullo stesso processo ha il proprio program counter e il proprio stack e condivide con gli altri threads l’heap e la memoria statica del processo. In Android si usano oggetti java.lang.thread che implementano l’interfaccia Runnable. Per usare un thread bisogna:

  • (^) creare un oggetto Thread
  • (^) chiamare il metodo start() del thread che a sua volta chiamerà il metodo run()

Ci sono due problemi però. Il primo è che il comune uso dei thread non è molto semplice da usare in maniera corretta. Il secondo è che, in Android, da un thread secondario non è possibile modificare l’interfaccia utente senza usare opportuni meccanismi di comunicazione, solo il main thread può modificare l’interfaccia utente. App esempio “ThreadNo”, “ThreadSi” Il framework offre un’alternativa che permette di usare un thread secondario in maniera corretta senza il problema di dover gestire la comunicazione tra thread: la classe AsyncTask.

AsyncTask

Viene invocato il metodo start() al cui interno si istanzia la classe BackgroundTask e la si manda in esecuzione direttamente. La classe BackgroundTask estende AsyncTask. I metodi contenuti nella classe BackgroundTask sono di due tipi diversi. Il metodo doInBackground è l’unico di quelli implementati che viene eseguito su un thread secondario in cui collocheremo tutte le operazioni “lente”. Al contrario, onPreExecute, onPostExecute e onProgressUpdate sono eseguiti sul thread principale e si occupano della comunicazione tra thread. Rispettivamente:

  • onPreExecute: inizializza le operazioni prima che avvenga l’esecuzione di doInbackground. Nello specifico prepara la ProgressDialog (es. progressBar) e la mostra;
  • onPostExecute: viene eseguito alla fine di doInBackground ed anch’esso svolge operazioni collegate all’interfaccia utente (es. l’apparizione di un Toast);
  • onProgressUpdate: viene invocato ogni volta che dall’interno di doInBackground viene chiamato publishProgress. Serve a fornire aggiornamenti periodici all’interfaccia utente ad esempio può spostare la barra di progresso in avanti di un passo. Un altro aspetto di AsyncTask cui si deve prestare attenzione sono i parametri della classe <Params, Progress, Result> di AsyncTask:
  • (^) Params: tipo di dati per il lavoro che deve svolgere il background thread;
  • (^) Progress: tipo di dati per lo stato di avanzamento;
  • (^) Result: tipo di dati per il risultato del task. Questi tre tipi di dato saranno, rispettivamente, il tipo di dato accettato in input dai metodi doInBackground, onProgressUpdate,onPostExecute. App esempio “ThreadAsyncTask”

Fragments

Un Fragment è una porzione di Activity. Non si tratta solo di un gruppo di controlli o di una sezione del layout. Può essere definito più come una sub-activity con un suo ruolo funzionale molto importante ed un suo ciclo di vita. Un Fragment non può vivere senza un’Actvity. Il Fragment ha il suo ciclo di vita fortemente collegato con quello dell’Activity di appartenenza. Esso può essere inserito nell’activity staticamente (nel layout del file) o dinamicamente (con il FragmentManager). Quando un Fragment viene inserito diventa un ViewGroup all’interno della View dell’activity. La sequenza di stati che scandiscono il ciclo di vita del fragment è simile a quella dell’activity, differisce nella fase di inizializzazione:

  • (^) onAttach : segnala il momento in cui il Fragment scopre l’Activity di appartenenza. Attenzione che a quel punto l’Activity non è stata ancora creata quindi si può solo conservare un riferimento ad essa ma non interagirvi;
  • (^) onCreate : è la creazione del Fragment in quanto componente;
  • (^) onCreateView : è il momento in cui viene creato il layout del Fragment. Solitamente qui si fa uso del LayoutInflater (es. GridView riempito con Adapter) ;
  • (^) onActivityCreated : segnala che la creazione dell’Activity è stata completata, vi si può interagire in tutto e per tutto. Gli altri metodi di callback del ciclo di vita vengono chiamati in corrispondenza degli omonimi metodi dell’Activity. App esempio “FragmentsExample”

Con la classe HttpUrlConnection possiamo recuperare l’InputStream in due modi:

  • (^) recuperando direttamente lo stream ai contenuti: InputStream is= url.openStream();
  • (^) oppure passando per la connessione: HttpURLConnection conn= (HttpURLConnection) url.openConnection(); InputStream is= conn.getInputStream(); Poi si procede come per i socket leggendo i dati dallo stream. Per comunicare in Internet mediante la libreria HttpClient sono utili le seguenti classi:
  • (^) AndroidHttpClient che implementa la classe HttpClient e ci permette di stabiilre una connessione HTTP;
  • (^) HttpGet che ci permette di inviare una richiesta GET al server;
  • (^) HttpResponse (responseHandler) che ci permette di leggere la risposta del server. Esempio: HttpClient request = new DefaultHttpClient(); HttpGet get = new HttpGet("http://www.mioserver.it/storage/file.txt"); HttpResponse response= request.execute(get); InputStream is= response.getEntity().getContent(); In tutti i casi, si è ottenuto un riferimento ad un InputStream. Per suo tramite, si potrà fare accesso ai contenuti del file. Grazie all’astrazione offerta dagli Stream i contenuti potranno essere recuperati come se il file fosse locale. App esempio “SocketURL”, “SocketHTTP” Le informazioni nelle pagine HTML sono difficili da estrarre, per questo ci sono delle librerie che implementano il parsing di documenti HTML tra cui troviamo la libreria JSOUP. Jsoup è parser HTML 5 le cui API consentono di estrarre dati e manipolare in maniera estremamente semplice documenti sfruttando le potenzialità di DOM, CSS e metodi di accesso simili a quelli offerti da JQuery.

In Jsoup un documento xml è rappresentato dalla classe Document, i nodi del documento da

istanze della classe Node e i tag da istanze della classe Element

La classe JSoup permette l’estrazione di singole parti del documento. Esempio: Document doc = Jsoup.connect(“http://www.url.it/“).get(); Element e = doc.getElementById(“id”); App esempio “SocketJSoup”

Data Storage

Android fornisce diverse opzioni per salvare i dati persistenti delle applicazioni. La soluzione scelta dipende dalle esigenze specifiche, ad esempio se i dati devono essere privati all’applicazione o accessibili ad altre applicazioni (e all'utente) e quanto spazio è necessario. Le opzioni sono le seguenti.

Shared Preferences App esempio “DataStoragePreferences”

La classe SharedPreferences permette di salvare e recuperare persistenti coppie chiave-valore dei tipi di dati primitivi. È possibile utilizzare SharedPreferences per salvare tutti i dati primitivi: boolean, float, int, long, e string. Questi dati rimarranno salvati per tutte le sessioni utente (anche se l'applicazione viene chiusa). Per ottenere un oggetto SharedPreferences si utilizza uno dei due metodi:

  • (^) getSharedPreferences(“filename”) - se si vogliono usare più file preferencies identificati per nome, che si specifica con il primo parametro.
  • (^) getPreferences() - se si usa un solo file preferences per l’Activity. Dato che c’è un solo file preferences per l’Activity non c’è bisogno di specificare il nome. I due metodi restituiscono un oggetto SharedPreferences obj. Per scrivere nello SharedPreferences:
  1. Serve un editor SharedPreferences.Editor editor = obj.edit()
  2. Si aggiungono valori con ad esempio putBoolean(), putString(), …
  1. Si salvano i nuovi dati aggiunti con editor.commit() Per leggere i valori si usano i metodi di SharedPreferences come ad esempio getBoolean() e getString(): Boolean v = obj.getBoolean("KEY");

Internal Storage App esempio “DataStorageInt”

Ogni app ha a disposizione uno spazio disco, detto Internal Storage che risiede in una parte del filesystem e a cui solo l’applicazione dovrebbe accedere. Il percorso in Android che porta in questa posizione è /data/data/package_java_della_applicazione/files. Il percorso è in formato Linux quindi lo ‘/’ iniziale indica la root del filesystem. Se l’app viene disinstallata la directory viene eliminata. es. package dell’app: it.unisa.apps => internal storage: /data/data/it.unisa.apps/files. Per accedere allo storage interno si usano per lo più due metodi, entrambi appartenenti al Context:

  • (^) Lettura: openFileInput (String filename): restituisce un FileInputStream, apre un file in lettura. Come parametro in input viene passato il nome del file. Non serve specificare il percorso in quanto sarà obbligatoriamente quello messo a disposizione dallo storage interno;
  • (^) Scrittura: openFileOutput (String filename, int mode): restituisce un FileOutputStream, apre uno stream in scrittura anche questo nello storage interno. Per il nome del file, vale quanto detto per l’input. Il secondo parametro, si può impostare alla costante Context.MODE_APPEND per concatenare i nuovi contenuti a quelli già esistenti nel file. Sono disponibili anche due costanti MODE_WORLD_READABLE e MODE_WORLD_WRITEABLE che servono a rendere il file accessibile anche al di fuori dello storage interno. Questi valori sono stati deprecati in quanto non in linea con il principio di riservatezza dei dati interni all’applicazione. Una volta ottenuto uno Stream, in input o in output che sia, va gestito come normale classe Java per procedere alle operazioni, rispettivamente, di lettura o scrittura. Creare e scrivere in un file nello storage interno:
  1. Chiamare openFileOutput(fileName, mode)
  2. Scrivere nel file con write()
  3. Chiudere lo stream con close() Leggere un file nello storage interno:
  4. Chiamare openFileInput(fileName)
  5. Leggere dal file con read()
  6. Chiudere lo stream con close() Altri metodi usati nell’ambito di internal storage:
  • getFilesDir(): restituisce la directory privata dell’app (dove vengono salvati i file)
  • (^) getDir(): crea (o apre se esiste) una directory all’interno dello spazio privato dell’app
  • (^) deleteFile(): cancella un file nello spazio privato
  • (^) filelist(): restituisce un array di file presenti nello spazio privato File temporanei Se si desidera memorizzare nella cache alcuni dati, piuttosto che conservarli in maniera persistente, è necessario utilizzare getCacheDir() per aprire un file che rappresenta la directory interna in cui l'applicazione deve salvare i file di cache temporanei.

Tutti gli altri valori indicano situazioni problematiche da valutare a seconda delle circostanze. Una volta controllato lo stato del supporto e del relativo filesystem, ci si può accedere. L’accesso alla cartella root dello Storage esterno primario si ottiene con il metodo statico: File getExternalStorageDirectory(). Dal riferimento all’oggetto File ottenuto, è possibile, secondo le procedure Java, leggere i contenuti, lavorare sui dati e via dicendo. È sconsigliabile salvare file direttamente nella cartella principale dello storage esterno, normalmente esso contiene delle cartelle associate alle principali tipologie di contenuti: Alarms, per i suoni da abbinare agli allarmi; Download, per i file scaricati; Movie, per i film; Music, per i file musicali; Pictures, per le foto; ecc… Ciò non impedisce ovviamente che ve ne possano essere altre. Per condividere dei file con altre app si usa getExternalStoragePublicDirectory(type), passando come parametro il tipo di directory che si desidera, es. DIRECTORY_MUSIC, DIRECTORY_PICTURES, DIRECTORY_RINGTONES, o altro. Salvando i file nella directory del tipo corrispondente, il media-scanner di sistema può correttamente classificare i file nel sistema (per esempio, le suonerie appaiono in impostazioni di sistema come suonerie, non come musica). Esempio: metodo che crea una nuova dir per delle foto nella dir pubblica delle immagini App esempio “DataStorage”

Database

Android fornisce il supporto completo per i database SQLite. Tutti i database creati saranno accessibili per nome a qualsiasi classe nell'applicazione, ma non al di fuori dell'applicazione. Il metodo consigliato per creare un nuovo database SQLite è quello di creare una sottoclasse di SQLiteOpenHelper e sovrascrivere il metodo onCreate (), in cui è possibile eseguire un comando SQLite per creare le tabelle del database.

Quindi si crea un nuovo Helper: dbHelper= new DatabaseOpenHelper(this); Dal quale si ricava un database: SQLiteDatabase db= dbHelper.getWritableDatabase();

  • onCreate: viene invocato nel momento in cui non si trova nello spazio dell’applicazione un database con nome indicato nel costruttore. Da ricordare che onCreate verrà invocato una sola volta, quando il database non esiste ancora. Il parametro passato in input è un riferimento all’oggetto che astrae il database. La classe SQLiteDatabase è importantissima in quanto per suo tramite invieremo i comandi di gestione dei dati. Il metodo onCreate contiene la query SQL che serve a creare il contenuto del database. Questo è l’applicazione del primo step,

enunciato prima. Notare che al suo interno non c’è alcun comando CREATE DATABASE in

quanto il database stesso è già stato creato dal sistema. Il comando SQL di creazione verrà

invocato mediante execSQL;

  • onUpgrade: viene invocato nel momento in cui si richiede una versione del database più aggiornata di quella presente su disco. Questo metodo contiene solitamente alcune query che permettono di adeguare il database alla versione richiesta. App esempio “DataStorageSQL”

Grafica e Animazioni

Quando si scrive un'applicazione, è importante considerare esattamente quali sono le esigenze grafiche. Ogni attività grafica deve essere realizzata con tecniche appropriate. Ad esempio, grafiche e animazioni per un'applicazione piuttosto statica devono essere attuate in modo molto diverso rispetto agli elementi grafici e animazioni per un gioco interattivo. Un’immagine può essere disegnata in due modi:

  • (^) oggetto View: quando la grafica è semplice e non necessita di cambiamenti;
  • (^) oggetto Canvas: quando la grafica è complessa e necessita di aggiornamenti frequenti. Un Drawable è un'astrazione generale per "qualcosa che si può disegnare” (es. un’immagine, un colore, una forma, …). La classe Drawable viene estesa per definire tipi più specifici:
  • (^) ShapeDrawable: una forma;
  • (^) BitmapDrawable: una matrice di pixel;
  • (^) ColorDrawable: un colore;
  • (^) Altre classi e classi personalizzate. Ci sono tre modi per definire e un'istanza di un Drawable:
  • (^) utilizzando un'immagine salvata nelle risorse del progetto;
  • (^) utilizzando un file XML che definisce le proprietà Drawable;
  • (^) utilizzando i normali costruttori della classe. L’oggetto Drawable deve essere inserito nella View e lo si può fare in due modi:
  • (^) direttamente nel file XML
  • (^) in modo programmatico View.setImageDrawable(R.id.nomeDrawable) Android fornisce due sistemi di animazione: Property Animation e View Animation. Entrambi i sistemi di animazione sono opzioni valide. In aggiunta a questi due sistemi, è possibile utilizzare la Drawable Animation , che consente di caricare le risorse disegnabili e li visualizza un fotogramma dopo l’altro. È possibile utilizzare il sistema View Animation al fine di effettuare animazioni interpolate su View. Questo tipo di animazioni sono descritte con file XML e possono eseguire una serie di trasformazioni semplici (rotazione, traslazione, scaling (dimensione), trasparenza) sui contenuti di un oggetto View. Quindi, ad esempio, se abbiamo un oggetto TextView, è possibile spostare, ruotare, scalare il testo all’interno. Se il testo ha un'immagine di sfondo, essa verrà trasformata con il testo. La classe Animation permette di leggere le animazioni dai file XML e applicarle alle ImageView. App esempio “GraficaImgAnim”

Custom Views

Il framework Android ha un ampio insieme di classi di visualizzazione (widgets) per l'interazione con l'utente e la visualizzazione di vari tipi di dati. Ma a volte un’applicazione ha esigenze

Disegnare la Custom View

La view una volta posizionata tramite il meccanismo di layout verrà disegnata mediante il metodo onDraw(Canvas canvas)sovrascritto dalla sottoclasse. In sintesi per creare una Custo View bisogna creare una sottoclasse di View ovvero una classe che estende View e sovrascrive i metodi onMeasure(), onLayout e onDraw(). App esempio “GraficaCustomWidget”, “GraficaCanvas”

Eventi touch e Multitouch

Un oggetto MotionEvent rappresenta un movimento registrato (pointer) da una periferica (dita, penna, mouse, …) rappresentato da informazioni:

  • (^) Gli ACTION_VALUES ovvero posizione e proprietà del movimento (sorgente, pressione, …);
  • (^) l’ ACTION_CODE (tipologia dell’evento):
    • ACTION_DOWN un dito tocca lo schermo ed è il primo
    • ACTION_POINTER_DOWN un dito tocca lo schermo ma non è il primo
    • ACTION_MOVE un dito che è sullo schermo si muove
    • ACTION_POINTER_UP un dito che è sullo schermo non lo tocca più
    • ACTION_UP l’ultimo dito sullo schermo viene alzato Un display multitouch permette il rilevamento di uno o più tocchi (pointer). Nel caso di display multitouch un MotionEvent può rappresentare più pointer, in tal caso possiamo accedere ai singoli pointer usando un ID unico per tutto il tempo in cui esistono. Esempio. Un MotionEvent può essere gestito mediante i suoi metodi:
  • getActionMasked() restituisce l’ACTION_CODE dell’evento
  • (^) getPointerCount() numero di pointer coinvolti
  • getActionIndex() fornisce l’indice del puntatore che viene trattato in questa singola invocazione di onTouchEvent ;
  • (^) getPointerID(int pointerIndex)restituisce l’id del singolo pointer indicato dall’indice passato in input, solitamente fornito da getActionIndex ;
  • (^) getX(int pointerIndex)
  • (^) getY(int pointerIndex)
  • (^) findPointerIndex(int pointerId) Il metodo onTouchEvent è un metodo della View che notifica alla View se l’evento è stato consumato o meno, se è stato consumato restituisce true. Il metodo onTouch viene invocato quando si verifica un evento prima che la view venga notificata. Come onTouchEvent restituisce un boolean. (Data la fondamentale importanza che la gestione del touchscreen riveste non sarà necessario implementare alcuna interfaccia ma il metodo onTouchEvent è “di serie” su qualsiasi View e sulle Activity) App esempio “MultitouchExample” Spesso si ha la necessità di gestire una combinazione di eventi (es. doppio click, scorrimento, …). La classe GestureDetector permette di riconoscere dei gesti fatti sul display. Tra i gesti riconosciuti abbiamo:
  • (^) pressione semplice;
  • (^) doppia pressione;
  • (^) fling (scorrimento); E’ possibile definire dei gesti personalizzati tramite un tool di Android e poi riconoscerli tramite il GestureDetector. Per riconoscere un gesto tramite il GestureDetector bisogna crearne uno che implementa l’interfaccia GestureDetector.onGetureListener e sovrascrivere il metodo onTouchEvent chiamato in risposta ad un gesto. Tale metodo delega il riconoscimento del gesto al metodo GestureDetector.onGestureListener. App esempio “GestureFlip”

Mediaplayer

Per riprodurre audio e video in Android si usano le seguenti classi:

  • la classe Mediaplayer che è usata per controllare la riproduzione di file audio/video.
  • l’ AudioManager che è un servizio di sistema, richiamabile quindi nel seguente modo: AudioManager manager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); Offre diversi metodi per configurare rapidamente vari aspetti dell’audio. tra cui:
  • (^) setRingerMode : permette di impostare la modalità di ring del dispositivo. Richiede in input un valore intero da scegliere tra valori costanti:
    • RINGER_MODE_SILENT (silenzioso),
    • RINGER_MODE_NORMAL (normale suoneria),
    • RINGER_MODE_VIBRATE (con vibrazione);
  • (^) adjustVolume : permette di regolare il volume. La direzione della variazione di volume va descritta con apposite costanti:
    • ADJUST_LOWER (per diminuire),
    • ADJUST_RAISE (per aumentarlo).

Ciclo di vita di un Mediaplayer

Quando un Mediaplayer viene creato attraverso il suo costruttore, si trova in uno stato IDLE, in cui si può trovare anche a seguito dell’invocazione del metodo reset(). Questo significa che è stata creata una sua istanza, la quale però non ha conoscenza del tipo di media da riprodurre, infatti sono state allocate solo le risorse indipendenti dal tipo di media che, se non utilizzate, sarebbe bene liberare attraverso l’invocazione del metodo release(), portando il player nello stato END. Per notificare al player il particolare media da riprodurre è possibile utilizzare diversi metodi a seconda della provenienza delle informazioni facendo l’overload del metodo setDataSource() che prevedono come parametri o l’identificatore di un file oppure l’URI/URL della particolare risorsa.

rinascere attraverso l’invocazione del metodo start() o portarsi nello stato STOPPED mediante l’invocazione del metodo stop(). Operazioni non possibili durante l’attraversamento degli stati generano degli errori di cui si può ricevere notifica tramite l’implementazione dell’interfaccia MediaPlayer.onErrorListener. Il MediaPlayer ci indica la posizione di riproduzione del media attraverso il metodo getCurrentPosition(), inoltre possiamo posizionarci in una particolare posizione del media attraverso il metodo seekTo() specificando come parametro la posizione rispetto all’istante iniziare in millisecondi. Un MediaPlayer può consumare preziose risorse di sistema. Pertanto quando non lo si utilizza più si dovrebbe sempre chiamare release() per assicurarsi che tutte le risorse di sistema ad esso assegnate siano rilasciate. Sui dispositivi c’è una sola uscita audio e ci possono essere diversi servizi multimediali concorrenti per il suo utilizzo. Ad esempio, quando un utente sta ascoltando musica e un'altra applicazione deve informare l'utente di qualcosa di molto importante, l'utente potrebbe non sentire il tono di notifica a causa della musica ad alto volume. Il meccanismo per le applicazioni di negoziare l’uso dell’uscita audio del dispositivo è chiamato Audio Focus. Quando l'applicazione ha bisogno di dare in uscita un audio si dovrebbe sempre richiedere l’audio focus. Una volta ottenuto il focus, l’applicazione può utilizzare l'uscita audio liberamente, ma deve sempre stare in ascolto di modifiche all’audio focus. Nel caso di perdita del focus audio, dovrebbe immediatamente smettere la riproduzione o abbassare il volume e ritornare a riprodurre o alzare il volume al momento della ripresa dell’audio focus.E’ compito dello sviluppatore l’uso dell’Audio Focus in modo da avere una buona user experience. App esempio “MusicPlayer”

Sensori

I dispositivi Android grazie ai sensori di cui sono forniti riescono a percepire movimenti, condizioni ambientali. i sensori possono essere suddivisi in tre grandi gruppi:

  • (^) sensori di movimento : percepiscono le forze fisiche che agiscono sul dispositivo. Ad esempio, l’accelerometro, il giroscopio, sensore di gravità;
  • (^) sensori ambientali : rilevano particolari dell’ambiente in cui ci si trova: temperatura, pressione, umidità;
  • (^) sensori di posizione : raccolgono dati sulla posizione del dispositivo, ad esempio il sensore di orientamento. Inoltre, i sensori, dipendentemente dal modo in cui sono implementati, possono essere hardware o software. I primi corrispondono a dei veri e propri elementi elettronici inseriti nel dispositivo. I secondi sono delle elaborazioni basate sui dati raccolti dai sensori hardware. Alcuni sensori devono essere necessariamente hardware, altri esistono solo software mentre alcuni possono essere hardware o software a seconda dell’implementazione che è stata scelta per il particolare dispositivo.

Il sottosistema Android che vogliamo sfruttare ci viene dischiuso da un system service , accessibile mediante la classe SensorManager. Il SensorManager permette di accedere alle informazioni dei sensori. L’insieme dei sensori disponibili in un dispositivo di ottiene con l’invocazione del metodo List getSensorList(int type) Il parametro indica il tipo di sensore ad esempio TYPE_ALL che permette di ottenere tutti i sensori disponibili (di ogni tipo). Leggere dati da un sensore Per l’acquisizione vera e propria delle informazioni la classe che utilizza il SensorManager deve implementare SensorEventListener Per controllare se un sensore esiste e ottenerne un riferimento Un po’ tutti i dispositivi avranno a disposizione almeno tre o quattro sensori essenziali per la vita di uno smartphone tra cui accelerometro, orientamento e rotazione. Gli aspetti da notare dell’Activity sono:

  • nel metodo onCreate è stato prelevato un riferimento al SensorManager. Opzionalmente questo punto sarà buono per recuperare un riferimento anche al sensore specifico con cui si vuole interagire;
  • (^) nei metodi onPause e onResume che come sappiamo regolano l’inizio e la fine dell’interazione tra Activity e utente avviene, rispettivamente, la registrazione e la cancellazione del listener;
  • (^) l’Activity implementa l’interfaccia SensorEventListener che forza all’override di due metodi onAccuracyChanged e onSensorChanged. Il metodo onSensorChanged costituisce il cuore dell’interazione con il sensore. È qui che arrivano le chiamate del listener ogni volta che sono disponibili nuove misurazioni. L’evento notificato verrà formalizzato con un oggetto di classe SensorEvent che permette di leggere i valori recuperati come un array numerico. Il metodo onAccuracyChanged consente di essere notificato di una variazione di accuratezza da parte del sensore passato come parametro. App esempio “Accelerometro”

Notifications

Si manifestano con una piccola icona che appare nella cosiddetta “Notification Area” e se ne può consultare il contenuto aprendo il “Notification drawer”, una zona “a scomparsa” sul display. Di seguito i vari elementi che costituiscono una comune notifica.

  1. titolo della notifica ( content title );
  2. icona grande ( large icon );
  3. contenuto della notifica ( content text );
  4. informazioni accessorie ( content info );
  5. icona piccola ( small icon ) che di norma appare anche nella barra del display;
  6. ora della notifica ( when ) impostata dal programmatore o di default dal sistema.