La presente lezione utilizza la libreria HTTPClient
per la connessione alla rete. Nonostante questa resti un'ottima soluzione per assolvere tali compiti e sia molto apprezzata tra gli sviluppatori Java, è stata deprecata a partire dalla versione 23 delle API Android. Pertanto, il suo utilizzo resta qui illustrato al solo fine di agevolare il mantenimento di progetti Android esistenti, in considerazione del largo impiego che ne è stato fatto nelle versioni precedenti del sistema.
Oltre al puro scaricamento di file, l'interazione con la Rete offre grandissime potenzialità. Una fra tutte: lo sfruttamento di servizi Web. Ormai non è più una novità parlare di Web Services. Si tratta essenzialmente di funzionalità rese disponibili in Rete da servizi remoti dai quali client distribuiti nel mondo possono recuperare informazioni, richiedere elaborazioni e molto altro ancora. Uno stile di servizio Web che si è diffuso molto già da diversi anni è REST (REpresentational State Transfer). Il motivo di ciò è la semplicità con cui può essere implementato e la diffusione dei concetti che ne sono alla base.
Introduzione ai servizi REST
Il funzionamento di questi servizi si basa sulla possibilità di sfruttare risorse disponibili in Rete mediante i classici metodi del protocollo HTTP. Di questi, i più comuni sono GET e POST, vecchia conoscenza degli sviluppatori web: il primo concepito per leggere dati da remoto senza apportare modifiche, il secondo per inviare dati verso il servizio con lo scopo di richiederne l'inserimento nella base dati. Oltre a questi, HTTP possiede altri metodi ed in particolare due verranno coinvolti nel discorso: PUT per richiedere la modifica dei dati e DELETE per averne la cancellazione.
Si noti che questi quattro metodi – POST, GET, PUT, DELETE – richiamano gli stessi quattro concetti espressi dai metodi CRUD dei database: creazione, lettura, modifica e cancellazione.
In questo senso i servizi REST possono essere visti come un modo distribuito per gestire un database.
Inoltre per ognuno dei suddetti metodi, il servizio Web specificherà con apposita documentazione quali URL devono essere contattati.
I ContentProvider, visti nei capitoli precedenti, da un punto di vista concettuale possono essere considerati un'adozione della mentalità REST nella condivisione di informazioni nel sistema Android.
Altro concetto appartenente al protocollo HTTP, sono i codici di stato contenuti nella risposta. Tra i più comuni ricordiamo:
200 | OK |
400 | Bad Request |
403 | Forbidden |
404 | Not found |
500 | Internal Server error |
Avranno un ruolo importante nei servizi REST ma appartengono anche alla comune esperienza della navigazione Internet: quante volte l'invocazione di un indirizzo web non corretto causava l'apparizione nel browser del classico messaggio “Error 404 – Page not found”?
In generale, al di là dei singoli codici, è importante ricordare che se la prima cifra del codice è 2 significa che comunque l'esecuzione è andata in porto, se è 4 indica errore da parte del client nella richiesta, se è 5 indica l'occorrenza di un errore dal lato server.
Le risorse offerte da servizi REST possono essere rappresentati in una moltitudine di formati, non ce n'è uno ufficiale. L'ideale sarebbe usare non solo formati molto diffusi – XML o JSON – ma possibilmente prevedere la distribuzione della stessa risorsa in vari formati permettendo così la fruizione nella modalità preferita.
Nel prosieguo dell'articolo si approfondirà uno dei formati più comuni per servizi REST, ne vedremo le modalità di utilizzo in Android ed il suo impiego pratico in un servizio web. Stiamo parlando di JSON.
JSON in Android
JSON (Javascript Object Notation) è un formato stringa per la rappresentazione di dati organizzati in oggetti e array. Negli ultimi anni, soprattutto grazie alla sua semplicità e al suo largo impiego in Ajax, ha acquisito una notevolissima popolarità soprattutto a discapito di XML. In Android, se ne può fare uso e, come al solito, il sistema contiene tutto il necessario.
Molto spesso le stringhe in JSON contengono array di oggetti. Per farne il parsing si può apprezzare la comodità delle classi JSONArray e JSONObject disponibili nel package org.json.
Prendiamo la semplice stringa JSON che contiene tre oggetti. Ogni oggetto a sua volta contiene due stringhe, nome e cognome di una persona.
[
{
"nome":"Lucio",
"cognome":"Bianchi"
},
{
"nome":"Paolo",
"cognome":"Neri"
},
{
"nome":"Sergio",
"cognome":"Rossi"
}
]
Se nel nostro codice Android avessimo bisogno di acquisirne i dati, supponendo che l'array fosse nella stringa json
, potremmo scrivere le seguenti righe:
JSONArray array=new JSONArray(json);
String persone=new String[array.length()];
for(int i=0;i<array.length();i++)
{
String nome=array.getJSONObject(i).getString("nome");
String cognome=array.getJSONObject(i).getString("cognome");
persone[i]=nome+" "+cognome;
}
Come si vede il costruttore di JSONArray implementa direttamente il parsing della stringa passata e l'oggetto ottenuto è consultabile quasi come un array vero e proprio mediante i metodi length()
, per leggere la lunghezza, e getJSONObject che restituisce il JSONObject in una determinata posizione.
Ogni JSONObject ottenuto all'interno del ciclo può essere letto “in stile mappa” recuperando i valori in base alla chiave assegnata in JSON. Con semplici modifiche, l'esempio precedente può essere adattato a molti tipi di parsing.
Interagire con un servizio REST
A questo punto dovremmo avere a disposizione tutti i prerequisiti necessari per l'interazione con un servizio REST.
Riepiloghiamoli:
- concetti basilari su cos'è REST: una visione chiara di come viene impiegato HTTP con i suoi metodi e codici di stato;
- saper gestire anche in maniera basilare il parsing e la formattazione JSON o di altro formato che sia necessario usare;
- saper fare in modo che la nostra app riesca ad accedere alla Rete e dialogare in HTTP. Abbiamo visto almeno due modi per farlo: classe Java tradizionale HttpURLConnection, meno funzionalità ma molto comuni e libreria Apache HttpClient, completissima e già inclusa in Android;
- ricordare sempre le due regole base per l'accesso alla Rete da Android: dichiarare le permission adeguate (almeno android.permission.INTERNET) ed eseguire gli accessi da un thread secondario. Per il secondo punto, nella maggior parte dei casi, è sufficiente saper usare AsyncTask.
A titolo di esempio, immaginiamo che ci sia un servizio REST che all'URL http://www.mioservizio.it/persone, via GET, restituisca in JSON un array di oggetti. Ogni oggetto rappresenta una persona specificandone nome,cognome ed età.
Vediamo le porzioni di Java che servirebbero a sfruttare entrambe le funzionalità. Ricordiamo per l'ennesima volta che il seguente codice va usato, previo inserimento della permission INTERNET nel manifest, all'interno di un metodo doInBackground di AsyncTask.
String url="http://www.mioservizio.it/persone";
String[] persone=null; // conterrà i risultati
HttpClient request=new DefaultHttpClient();
HttpGet get=new HttpGet(url);
HttpResponse response=request.execute(get);
responseCode=response.getStatusLine().getStatusCode();
if (responseCode==200)
{
InputStream istream=response.getEntity().getContent();
BufferedReader r=new BufferedReader(new InputStreamReader(istream));
String s=null;
StringBuffer sb=new StringBuffer();
while((s=r.readLine())!=null)
{
sb.append(s);
}
JSONArray array=new JSONArray(sb.toString());
persone=new String[array.length()];
for(int i=0;i<array.length();i++)
{
String nome=array.getJSONObject(i).getString("nome");
String cognome=array.getJSONObject(i).getString("cognome");
String eta=array.getJSONObject(i).getString("eta");
persone[i]=nome+" "+cognome+" di anni "+eta;
}
return persone;
}
Notare che, una volta effettuata la connessione ed ottenuta la risposta, è necessario leggere il codice di risposta HTTP. In questo caso, le operazioni di parsing vengono eseguite solo se il codice HTTP restituito è 200. Come spiegato in precedenza si possono attuare comportamenti differenti per codici di stato diversi.
Le operazioni di interpretazione dei risultati vengono effettuate aggregando gli elementi appresi in questa lezione e nelle precedenti:
- con
response.getEntity().getContent()
viene prelevato il contenuto dell'entity HTTP sotto forma di InputStream; - l'InputStream verrà letto iterativamente al fine di ritrovare tutto il suo contenuto all'interno di uno StringBuffer;
- lo StringBuffer, quando completo, conterrà un array JSON formato da un elenco di persone e pertanto se ne potrà fare il parsing con JSONArray;
- dopo il parsing, i dati prelevati dal servizio, saranno disponibili in una struttura dati Java – in questo caso un semplice array di stringhe – e quindi pronti per essere sfruttati nella propria app.
Gli altri metodi HTTP
L'esempio ha mostrato solo una lettura di dati via GET. Gli altri casi non vengono esemplificati nei dettagli ma si consideri che la libreria HttpClient oltre a HttpGet
contiene classi corrispondenti agli altri metodi HTTP: HttpPost
, HttpPut
e HttpDelete
.
Inoltre, mentre eventuali parametri nel GET vengono inviati concatenati all'URL in una querystring, negli altri metodi lo si può fare con liste di BasicNameValuePair come mostrato nel capitolo relativo all'accesso in Rete.
Lo studio può comunque essere proseguito sia sulla documentazione Android che della fondazione Apache in entrambi i casi molto completa.