In questo articolo vedremo come poter effettuare richieste HTTP(s) in modo asincrono con Android, per poter facilmente ricevere risposte in formato JSON.
Che cos’è JSON?
Il formato JSON è diventato molto popolare nel corso degli ultimi anni, in quanto permette facilmente di scambiare oggetti tra diversi software, senza problemi di sintassi. Ad esempio è possibile inviare una richiesta in formato JSON da un dispositivo Android ad un server PHP.
Ecco un semplice file JSON:
{ "colorsArray":[{ "colorName":"red", "hexValue":"#f00" }, { "colorName":"green", "hexValue":"#0f0" }, { "colorName":"blue", "hexValue":"#00f" }, { "colorName":"cyan", "hexValue":"#0ff" }, { "colorName":"magenta", "hexValue":"#f0f" }, { "colorName":"yellow", "hexValue":"#ff0" }, { "colorName":"black", "hexValue":"#000" } ] }
Per chi volesse avere ulteriori informazioni riguardo a questo utilissimo formato, ecco un po’ di link:
Che cos’è HTTP(s)?
HTTP è l’acronimo di HyperText Transfer Protocol ed il protocollo di comunicazione più importante e più usato. Esso permette, in un’architettura client-server, di poter richiedere informazioni ad un determinato dispositivo e quest’ultimo provvederà a rispondere. Tale protocollo viene usato ogni volta che usiamo il nostro browser. La (s) sta per secure e denota la versione sicura del protocollo.
Per una completa definizione di HTTP, è possibile consultare la pagina di Wikipedia a questo link.
HTTP+JSON
Il protocollo HTTP e il formato JSON sono molto usati quando si vogliono creare sistemi di comunicazione tra i dispositivo mobile, ad esempio Android/iOs etc… e un server. Il nome tecnico è API, in quanto il software presente sul server, ad esempio un programma scritto in PHP, mette a disposizione la possibilità di ricevere ed inviare informazioni, attraverso uno specifico formato. Ad esempio posso richiedere al server la lista dei colori disponibili e lui mi risponderà con un file JSON simile a quello dell’esempio. Ora vedremo un semplice programma per Android, che utilizzerà un po’ tutti gli argomenti che abbiamo appena trattato. Tale programma è il punto di partenza per poter creare progetti anche molto complessi, in quanto sono comunque basati su richiesti HTTP(s) e risposte JSON.
Programma che mostra l’IP pubblico
Lo scopo del programma è quello di mostrare l’IP pubblico in formato V4, attraverso una richiesta HTTP(s) di tipo GET al server https://api.ipify.org/?format=json, il quale ritornerà una risposta in formato JSON:
{"ip":"192.168.1.2"}
Ovviamente il valore all’interno della risposta cambierà da utente a utente, in quanto la richiesta viene fatta da dispositivi connessi a reti diverse.
Dal punto di vista più tecnico, o meglio pratico, il programma avrà la seguente grafica:
Il programma quindi, una volta premuto il button, mostrerà una scritta con l’indirizzo IP pubblico della rete a cui siamo connessi.
Requisiti software
- Android Studio IDE
- Libreria GSON
- Libreria Async Http Client
- Libreria ButterKnife
Libreria GSON
Questa libreria permette di trasformare un oggetto Java, ad esempio una persona, in un stringa in formato JSON e viceversa. Ecco un esempio:
Persona persona = new Persona("Mario", "Rossi", new Data(1, 1, 1980)); Gson gson = new Gson(); String jsonString = gson.toJson(persona); System.out.println(jsonString); Persona object = gson.fromJson(jsonString, Persona.class); System.out.println(object);
Libreria Async Http CLient
Questa libreria facilita di molto la vita del programmatore, in quanto permette di effettuare richieste HTTP(s) asincrone rispetto al thread principale dell’interfaccia grafica; questo fa si che l’applicazione non si blocchi e non sarà necessario riscrivere del codice. Ecco un esempio dell’utilizzo:
import com.loopj.android.http.*; import cz.msebera.android.httpclient.Header; AsyncHttpClient client = new AsyncHttpClient(); RequestParams params = new RequestParams(); params.put("key", "value"); params.put("more", "data"); client.get("http://www.google.com", params, new TextHttpResponseHandler() { @Override public void onSuccess(int statusCode, Header[] headers, String res) { // called when response HTTP status is "200 OK" } @Override public void onFailure(int statusCode, Header[] headers, String res, Throwable t) { // called when response HTTP status is "4XX" (eg. 401, 403, 404) } } );
Libreria ButterKnife
Questa libreria è molto utile quando ci sono tanti elementi grafici, che devono essere richiamati nel codice, all’interno delle varie activities. Dal punto di vista pratico, permette di ridurre il codice scritto, semplificando la comprensione. Ecco un esempio:
class ExampleActivity extends Activity { @Bind(R.id.title) TextView title; @Bind(R.id.subtitle) TextView subtitle; @Bind(R.id.footer) TextView footer; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.simple_activity); ButterKnife.bind(this); // TODO Use fields... } }
@OnClick(R.id.submit) public void sayHi(Button button) { button.setText("Hello!"); }
Creiamo il nostro progetto in Android Studio
Ora che abbiamo tutte le nozioni necessarie, possiamo finalmente realizzare il nostro progetto.
Per farlo apriamo Android Studio e creiamo un nuovo progetto, che nel mio caso ho chiamato ExampleAsyncHTTP:
Selezioniamo la versioni minima dell’API Android SDK supportata:
Nella schermata successiva, visto che abbiamo una sola Activity, selezioniamo il template Empty Activity:
Ed infine diamo il nome alla nostra activity, con il relativo layout:
Dopo qualche secondo, il nostro progetto verrà realizzato e mostrato all’interno di Android Studio.
Uno dei grandi vantaggi di Android Studio e la facilità con cui è possibile inserire/aggiungere librerie al progetto; per farlo basterà aprire il file build.gradle e inserire le seguenti righe:
compile 'com.loopj.android:android-async-http:1.4.9' compile 'com.jakewharton:butterknife:7.0.1' compile 'com.google.code.gson:gson:2.4'
Il file build.grandle, ogni volta che viene modificato, richiederà la sincronizzazione; infatti Android Studio dovrà scaricare le varie libreria dalla rete. Tale file avrà la seguente struttura:
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.example.exampleasynchttp" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.1' compile 'com.loopj.android:android-async-http:1.4.9' compile 'com.jakewharton:butterknife:7.0.1' compile 'com.google.code.gson:gson:2.4' }
Ora possiamo passare alla parte di programmazione del nostro progetto.
Il primo passo è quello di realizzare la classe Response, che contiene le informazioni riguardo alla risposta che il server ci fornirà. Ecco la sua struttura:
package com.example.exampleasynchttp; /** * This is the simple response given from the HTTP get request, to obtain the pubblic IP address of your device */ public class Response { private String ip; public Response(String ip) { this.ip = ip; } public String getIp() { return ip; } public void setIp(String ip) { this.ip = ip; } @Override public String toString() { return "The ip address is: " + ip; } }
Per quanto riguarda la classe MainActivity, sarà così composta:
package com.example.exampleasynchttp; import android.app.ProgressDialog; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.Button; import android.widget.Toast; import com.google.gson.Gson; import com.loopj.android.http.AsyncHttpClient; import com.loopj.android.http.RequestParams; import com.loopj.android.http.TextHttpResponseHandler; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import cz.msebera.android.httpclient.Header; public class MainActivity extends AppCompatActivity { @Bind(R.id.button) Button button; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); } @OnClick(R.id.button) public void showIP(Button button) { String url = "https://api.ipify.org/?format=json"; AsyncHttpClient client = new AsyncHttpClient(true, 80, 443); RequestParams params = new RequestParams(); final ProgressDialog pDialog = new ProgressDialog(MainActivity.this); client.get(url, params, new TextHttpResponseHandler() { @Override public void onStart() { pDialog.setMessage("Loading..."); pDialog.setIndeterminate(false); pDialog.setCancelable(true); pDialog.show(); } @Override public void onSuccess(int statusCode, Header[] headers, String responseString) { Gson gson = new Gson(); Response response = gson.fromJson(responseString, Response.class); pDialog.dismiss(); Toast.makeText(getApplicationContext(), response.toString(), Toast.LENGTH_LONG).show(); } @Override public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) { pDialog.dismiss(); Toast.makeText(getApplicationContext(), responseString, Toast.LENGTH_LONG).show(); } }); } }
Il codice è abbastanza semplice da capire, grazie all’utilizzo delle librerie citate sopra. Il button è quello presente nell’interfaccia grafica, il quale viene richiamato nel codice quando viene premuto. In tale caso, viene fatta una richiesta asincrona con il server, che restituirà una risposta in formato JSON, che verrà convertito in un oggetto Java di tipo Reponse, grazie alla libreria GSON. Viene usata una ProgressDialog, come elemento grafico d’attesa della risposta dal server.
Ecco il file XML del layout della MainActivity:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:orientation="vertical" android:gravity="center" tools:context="com.example.exampleasynchttp.MainActivity"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:textSize="20dp" android:text="Push the button to see your IP!" /> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/button" android:text="Click me"/> </LinearLayout>
L’unico passaggio che rimane prima di mandare in esecuzione il nostro programma, è quella di inserire i permessi per poter far accedere lo smartphone Android ad Internet. Per farlo dobbiamo aprire il file manifest, che avrà una struttura simile a questa:
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.exampleasynchttp"> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
Per poter scaricare l’intero progetto, ecco qui il link di Github.