Creare App per inviare e ricevere dati in GET e POST tramite HTTP
La maggior parte delle APP Android che usi ogni giorno, riescono a comunicare con pagine Web o Web Services remoti per scambiarsi informazioni. In questo tutorial, imparerai a realizzare la sezione di un'app in grado di inviare in rete tramite HTTP, dei dati inseriti dall'utente.
Ti servono: 25 minuti
Creare applicazioni in Java per Android, è un'attività che spesso necessita di dover comunicare con pagine web o servizi risiedenti su web server. Ad esempio, se sei amante dei social network, tutte le volte che clicchi su "Mi piace", tutte le volte che invii un tuo post sul diario, usando il tuo smartphone, non fai altro che inviare queste informazioni ad un server remoto, tramite qualche protocollo spesso HTTP, che avrà cura di memorizzarle su di un database remoto, in modo che chiunque possa vedere questi aggiornamenti in tempo reale.
Spesso queste informazioni vengono passate ad un Web Service RESTful, implementato nel server che riceverà le informazioni/dati dalla tua app.
Con l'avvento della versione 6 di Android (Android MarshMallow), sono stati eliminati i supporti alle classi org.apache.http
e android.net.http.AndroidHttpClient
, precedentemente utilizzate per la comunicazione con il web, quindi se vuoi creare un'app che possa essere installata su dispositivi con API 23 e superiori, dovrai conoscere queste nuove classi.
In alternativa, puoi continuare ad usare le vecchie classi, al solo scopo di test dell'applicazione, potrai modificare il file buidl.gradle dell'applicazione, aggiungendo nel nodo android, la riga: useLibrary 'org.apache.http.legacy'
Con le indicazioni riportare in questa guida, facilmente potrai riadattare la tua applicazione, in modo che possa usare la classe URLConnection.
Cosa imparerai:
In questo mini tutorial per la creazione di un'app di invio e ricezione dati tramite HTTP, vedremo come si possa inviare un'informazione, inserita all'interno di un campo di input EditText, ad una pagina remota creata nel linguaggio .php, e visualizzare la risposta fornita dalla pagina.
In una dei precedenti tutorial (Richiamare un'Activity da un'altra pagina con Intent), avevamo visto come passare da una pagina all'altra di una nostra applicazione, tramite l'inserimento dell'istruzione startActivity(), che riceveva come parametro di ingresso un intent esplicito corrispondente alla pagina che si voleva aprire.
Ora completeremo questa semplice applicazione, andando a progettare la pagina in modo che l'utente possa inserire la propria data di nascita, e al click sul bottone venga visualizzata la prima notizia apparsa in quel giorno, ricercando negli ipotetici archivi online del quotidiano "La gazzetta dell'Africa".
- Recuperare dati da un campo EditText tramite il metodo findViewById()
- Inviare dati in GET ad una pagina web tramite la classe HttpURLConnection e URL
- Recuperare la risposta di una pagina web tramite il metodo getInputStream() e mostrarla all'interno dell'app
- Creare un nuovo Thread per l'esecuzione dell'invio, grazie alla classe AsyncTask
1° Passo: definire l'Activity per l'inserimento della data di nascita
Questo passo penso tu lo conosca molto bene ormai. Dovremo definire un layout con all'interno un campo Editext e un Button, in modo da dare all'utente la possibilità di inserire la propria data di nascita e inviare il dato cliccando sul bottone. I nomi che useremo per identificare i due elementi, saranno per il primo campo "datadinascita" e per il secondo "ricerca". Qui sotto trovi il layout base che potremmo generare partendo da un LinearLayout, con campi centrati.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:gravity="center_vertical|center_horizontal"> <EditText android:text="" android:id="@+id/datadinascita" android:hint="Inserisci la tua data di nascita nel formato (dd-mm-yyyy)" android:layout_width="match_parent" android:layout_height="wrap_content"> </EditText> <Button android:text="Ricerca" android:id="@+id/ricerca" android:layout_width="wrap_content" android:layout_height="wrap_content"> </Button> </LinearLayout>
I punti salienti da ricordare sono che abbiamo usato l'attributo gravity="center_vertical|center_horizontal" per centrare sia il campo EditText sia il bottone al centro dello schermo, e questo indipendentemente dall'orientamento di questo. Il layout lo salveremo con il nome "activity_page1" sempre all'interno della cartella delle risorse Layout.
2° Passo: Gestire il click e recuperare il dato inserito
Dopo che l'utente hai inserito il dato, dovremo creare del codice in grado non solo di intercettare il click sul bottone ma anche recuperare il dato inserito dall'utente. Quest'ultima tecnica l'abbiamo già imparata in precedenti tutorial. Eventualmente ti invito a rileggere sia Gestire il tocco su di un bottone, che Richiamare un'Activity da un'altra pagina con Intent per avere un quadro completo dell'app che stiamo ultimando.
Per poter recuperare un dato inserito all'interno un campo Editext, si usano le stesse tecniche viste per individuare il bottone ossia si userà il metodo findViewById(), solo che oltre ad individuare il componente, è necessario anche estrarre il testo inserito.
Per questo si fa uso del metodo getText() e si trasforma il tutto il stringa con toString(). Questa stringa potrebbe contenere dei caratteri che è necessario convertire per creare un url valido, quindi lascio a te l'esercizio di effettuare la codifica della stringa con la classe URLEncoder.
Queste tecniche sono molto simile a quelle che si usano ad esempio in JavaScript o jQuery.
Quindi il codice completo sarà:
// identifico il campo EditText EditText lamiadata = (EditText) findViewById(R.id.datadinascita); // uso il metodo getText() per recuperare il testo inserito dall'utente e lo forzo ad essere una stringa String datainserita = lamiadata.getText().toString();
In questo caso abbiamo optato per recuperare il dato e memorizzarlo in una variabile d'appoggio in modo che possa essere preparato per l'invio al server, un pò come succedere per l'astronauta che viene preparato per essere "sparato" orbita. Nel nostro caso l'orbita sarà un web server o un servizio RESTfull che "accoglierà" appunto il dato.
Qui sotto trovi la prima parte del codice, compresa la gestione del click sul pulsante. La classe è stata nominata Page1, per mantenere gli stessi nomi usati nei precedenti tutorial di esempio.
public class Page1 extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_page1); // identifico il campo EditText EditText lamiadata = (EditText) findViewById(R.id.datadinascita); // identifico il bottone Button btnRicerca = (Button) findViewById(R.id.ricerca); btnRicerca.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // uso il metodo getText() per recuperare il testo inserito dall'utente e lo forzo ad essere una stringa String datainserita = lamiadata.getText().toString(); }); } } }
3° Passo: "Sparare" il dato in orbita in modalità GET!
Ora che abbiamo il dato, il passo successivo è quello di impostare tutti quegli strumenti che simulino la base di lancio per il mio razzo che avrà all'interno il dato. Per costruire un razzo, ossia impostare la richiesta di invio dati, si usano della classi che ricordano, a livello di nomi, proprio quello che accade non appena apri il browser, digiti un indirizzo, e clicchi invio. In particolare si useranno la classe HttpURLConnection con il suo metodo openConnection() e la classe URL.
La prima si può paragonare all'azione di apertura della finestra del browser, ossia all'utilizzo di uno strumento/oggetto che ci permette di scrivere l'indirizzo della pagina web che dovrà ricevere il dato. Essendo una classe astratta con metodi statici (vedi corso Avanzato di Java per Android), possiamo utilizzarla senza richiamare la "parola magica" new, e accedere direttamente ai suoi metodi, tra cui openConnection() il quale dovrà essere richiamato su un oggetto URL precedentemente creato sulla base dell'indirizzo della pagina che vogliamo aprire.
Per prima cosa allora, dobbiamo creare l'oggetto URL, che rappresenta l'indirizzo della pagina che vogliamo richiamare. Useremo proprio la classe URL().
// Creo l'oggetto URL che rappresenta l'indirizzo della pagina da richiamare URL paginaURL = new URL("http://www.corsoandroid.it/webservice/?datanascita=" + datainserita);
dove come parametro della query, abbiamo inserito il dato recuperato dal campo EditText.
La pagina che riceverà questa informazione, potrà essere sviluppata in un qualsiasi linguaggio, ad esempio PHP, oppure potremmo creare un apposito RESTful Web Services. Spiegare come realizzare una tale pagina o servizio va oltre lo scopo dell'articolo. Ti rimando eventualmente al Corso per programmare in PHP che puoi trovare sul sito nostro partner video-corsi.com
Poi sfrutteremo la classe HttpUrlConnection() e il metodo openConnection(), per aprire una connessione con la pagina remota:
// creo l'oggetto HttpURLConnection paragonabile all'apertura di una finestra del browser HttpURLConnection client = (HttpURLConnection) paginaURL.openConnection();
Il metodo usato per la richiesta della pagina è predefinito in GET.
3° Passo (bis): "Sparare" il dato in orbita in modalità "POST"!
Se volessimo richiamare la pagina in POST, quindi con invio di dati non più codificati nell'URL della pagina da chiamare, come visto con GET, ma inviati direttamente all'interno del corpo della richiesta, dovrei pima di tutto creare la stringa con le coppie chiave=valore, da inviare, e poi scrivere il dato nello stream di uscita, grazie alla classe Java OutputStreamWriter.
// codifico le coppie di dati da inviare String datiPost = URLEncoder.encode("datanascita", "UTF-8") + "=" + URLEncoder.encode(datainserita, "UTF-8");
A differenza di GET, dobbiamo anche richiamare il metodo setDoOutput(true) della classe HttpURLConnection(), aggiungendo quindi l'ulteriore istruzione:
// se devo inviare il dato in POST client.setDoOutput(true);
A questo punto scrivo il dato nello stream di Uscita:
OutputStreamWriter wr = new OutputStreamWriter(client.getOutputStream()); wr.write(datiPost); wr.flush();
E' consigliato inoltre aggiungere il metodo setChunkedStreamingMode(), al fine di ottimizzare la gestione del buffer di memoria, che memorizzerà temporaneamente, la risposta dal server.
// codifico le coppie di dati da inviare String datiPost = URLEncoder.encode("datanascita", "UTF-8") + "=" + URLEncoder.encode(datainserita, "UTF-8"); // se devo inviare il dato in POST client.setDoOutput(true); client.setChunkedStreamingMode(0); // scrivo nello stream di uscita OutputStreamWriter wr = new OutputStreamWriter(client.getOutputStream()); wr.write(datiPost); wr.flush(); wr.close();
A questo punto la pagina remota è stata chiamata, sia usando GET che usando POST, esattamente come se avessimo digitato l'url nel browser e cliccato invio.
Il codice quindi fino ad ora sviluppato, potrebbe essere scritto, con alcuni perfezionamenti, in questo modo (esempio GET):
public class Page1 extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.home); // identifico il campo EditText EditText lamiadata = (EditText) findViewById(R.id.datadinascita); // identifico il bottone Button btnRicerca = (Button) findViewById(R.id.ricerca); btnRicerca.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // recuperare il testo inserito dall'utente String datainserita = lamiadata.getText().toString(); // Creao l'oggetto URL che rappresenta l'indirizzo della pagina da richiamare URL paginaURL = new URL("http://www.corsoandroid.it/webservice/?datanascita=" + datainserita); // creo l'oggetto HttpURLConnection e apro la connessione al server HttpURLConnection client = (HttpURLConnection) paginaURL.openConnection(); // TODO: Mostro le informazioni } }); } }
4° Passo: Recuperare le informazioni inviate dal razzo!
A questo punto il dato è stato ricevuto e la pagina o il Web Service ora è in grado di rispondere e inviare indietro delle informazioni. Come faccio ad intercettarle e a mostrarle all'interno della mia app? Per intercettarle possiamo sfruttare sempre la classe HttpURLConnection() la quale ha definito tutta una serie di metodi, in grado di estrarre le informazioni legate alla risposta del server.
Quest'ultimo infatti, invia una risposta, costituita di diverse informazioni, tra cui la tipologia di dato inviato (testo, json, xml etc), il codice dello stato http della pagina (200,400,500), e il corpo vero e proprio del messaggio creato grazie al web service o pagina dinamica.
Chiaramente la formattazione dei dati ricevuti, dipenderà dal tipo di questi dati, se dati in formato json, o xml, o testuali. Per maggiori dettagli su come gestire questi dati e mostrarli all'interno di una lista, consulta il "Corso Avanzato di Java: Creare APP Android".
Tra i diversi metodi allora disponibili all'interno della classe, abbiamo getInputStream() il quale restituisce un oggetto InputStream che dovrà essere gestito per estrarre l'informazione leggibile. Nel nostro caso potemmo definire la funzione mostroDati() la quale riempirà leggerà riga per riga, l'intera sequenza di dati in ingresso.
public class Page1 extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.home); // identifico il campo EditText EditText lamiadata = (EditText) findViewById(R.id.datadinascita); // identifico il bottone Button btnRicerca = (Button) findViewById(R.id.ricerca); btnRicerca.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { // recuperare il testo inserito dall'utente String datainserita = lamiadata.getText().toString(); try { // Creao l'oggetto URL che rappresenta l'indirizzo della pagina da richiamare URL paginaURL = new URL("http://www.corsoandroid.it/webservice/?datanascita=" + datainserita); // creo l'oggetto HttpURLConnection e apro la connessione al server HttpURLConnection client = (HttpURLConnection) paginaURL.openConnection(); // Recupero le informazioni inviate dal server InputStream risposta = new BufferedInputStream(client.getInputStream()); } catch (Exception e) { e.printStackTrace(); } String datiLetti = mostroDati(risposta); Toast.makeText(getApplicationContext(), datiLetti, Toast.LENGTH_LONG).show(); } }); } private static String mostroDati(InputStream in) { StringBuilder sb = new StringBuilder(); try (BufferedReader reader = new BufferedReader(new InputStreamReader(in));) { String nextLine = ""; while ((nextLine = reader.readLine()) != null) { sb.append(nextLine); } } catch (IOException e) { e.printStackTrace(); } return sb.toString(); } }
5° Passo: Aggiungere le informazioni sui permessi dell'applicazione
Per poter accedere ad internet, la tua applicazione necessita dei permessi corretti. Dovrai quindi modificare il file manifest, aggiungendo questa riga di codice:
<uses-permission android:name="android.permission.INTERNET" />
Se a questo punto provassi a testare l'applicazione, questa potrebbe andare in errore. Il motivo è legato ai Thread e alla natura stessa delle comunicazioni tra il tuo dispositivo e un server remoto che possono avere dei ritardi. Le operazioni effettuate fino ad ora usano lo stesso Thread, quindi è necessario crearne un'altro, all'interno del quale trasferire le operazioni di invio informazioni e relativo recupero.
6° Passo: Creare un nuovo Thread separato dall'interfaccia UI principale
Il metodo più semplice per separare diverse azioni in grado di essere eseguite con la stessa interfaccia UI, è quello di utilizzare la classe AsyncTask. In sostanza, tutte le operazioni che vengono eseguite dopo il click sul pulsante, dovranno essere trasferite all'interno di una sottoclasse di AsyncTask ed eseguite all'interno di questa.
La peculiarità di questa classe è che avrà all'interno due metodi doInBackground() e onPostExecute(), all'interno dei quali potrò gestire le azioni di invio e quelle di ricezione.
Questa sottoclasse, la potremmo chiamare InvioDati:
private class InvioDati extends AsyncTask<String,Void,String> { @Override protected String doInBackground(String ... url) { // azioni di invio } @Override protected void onPostExecute(String result) { // azioni di lettura dati } }
e da richiamare con una notazione del tipo:
new InvioDati().execute(getURL);
2024-10-14 Tipo/Autore: Davide Copelli {ing} Pubblicato da: CorsoAndroid.it