Android 3 - Connessione a server

Comunicazione con un server

Immagine slide
  • In questa lezione finale toccheremo un altro argomento cruciale per un'app: il recupero di dati da un server.
  • Utilizzeremo come sorgente dati li feed RSS delle notizie della home del Fermi.
  • L'indirizo di questo feed è http://www.fermimn.gov.it/?action=rss
  • Da questo feed noi estrarremo solo i titoli, che inseriremo un un'area di testo, uno per riga, separati da una riga vuota

La Struttura della Sorgente Dati

Immagine slide
  • Un feed RSS è un file XML. Inizia con le seguenti righe:
    <?xml version="1.0" encoding="utf-8"?>
    <rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
    <title>Istituto Superiore Fermi, Mantova - NEWS</title>
    <link>http://www.fermimn.gov.it/</link>

La Struttura della Sorgente Dati

Immagine slide
  • Una notizia ha la seguente forma:
      <item>
    <title>AVANGUARDIE EDUCATIVE PORTA A MANTOVA IL PROF. DOURMASHKIN
    </title>
    <description>
    <![CDATA[
    ...
    ]]></description>
    <guid isPermaLink="true">...</guid>
    <link>...</link>
    <pubDate>Thu, 12 Feb 2015 08:00:00 +0100</pubDate>
    </item>

La Struttura della Sorgente Dati

Immagine slide
  • Come vedete, il titolo è in una singola riga che comincia con il tag <title>
  • Il tag </title> è sempre nella riga successiva
  • Troviamo un'altra riga con il tag <title> nell'intestazione, ma essa è chiusa con il tag </title> sulla stessa riga
  • Per isolare il titolo di ogni notizia sfrutteremo le funzioni di ricerca delle strinhe

La nuova APP

Immagine slide
  • Creeremo quindi una nuova APP, come al solito, con l'immagine di questa slide come icona
  • Questa app sarà molto semplice come aspetto
  • Possiederà un bottone per caricare le notizie
  • Possiederà un'area di testo scrollabile per contenere i titoli
  • Avrà anche una ProgressBar (inizialmente invisibile) da mostrare mentre si caricano i titoli.

Le stringhe

Immagine slide
  • Cominciamo come la solito dalle stringhe, che verranno richiamate in tutto il resto.
  • In questa app, oltre a quelle di base, avremo solo il testo del bottone ed il segnaposto per i titoli delle notizie

Le stringhe

Immagine slide
  • Quindi:
    <?xml version="1.0" encoding="utf-8"?> <resources>

    <string name="app_name">notizie</string>
    <string name="action_settings">Settings</string>
    <string name="button_carica">Carica i titoli</string>
    <string name="text_placeholder">Notizie...</string>

    </resources>

Il Layout

Immagine slide
  • Come si vede il layout prevede un bottone ed un'area di testo
  • Al suo interno, ed a riempirlo, ho aggiunto un LinearLayout
  • Ho lasciato il RelativeLayout, vedremo poi perché
  • Per rendere il testo scrollabile, l'ho inserito in una ScrollView.
  • Ho anche aggiunto una ProgressBar, inizialmente invisibile, per far vedere la rotellina che gira mentre vengono caricate le notizie.

Il Layout

Immagine slide
  • Questo elemento non è nel LinearLayout, ma nel RelativeLayout che lo contiene, in modo da essere sovrapposto al contenuto della videata
  • Il codice del layout è allegato alla slide

Il Manifest

Immagine slide
  • La nostra applicazione accede ad Internet
  • Per consentirle questo è indispensabile aggiungere nel Manifest il relativo permesso.
  • Dovremo ancare sulla tab Permissions del Manifest.
  • Potremo cliccare su Add..., selzionare UsesPermission e nella lista selezionare android.permission.INTERNET
  • La riga che viene aggiunta al manifest è questa:
        <uses-permission android:name="android.permission.INTERNET"/>

Gli attributi e la onCreate

Immagine slide
  • Per prima cosa vediamo la struttura generale dell'App.
  • Userò due attributi per accedere rapidamente alla TextView di testo ed alla ProgressBar:
        private TextView notizie;
    private ProgressBar attesa;
  • E naturalmente nella onCreate li assocerò agli elementi dell'interfaccia:
        notizie = (TextView)findViewById(R.id.textView_notizie);
    attesa = (ProgressBar)findViewById(R.id.progressBar);

Gli attributi e la onCreate

Immagine slide
  • Per quanto riguarda il bottone, richiameremo un metodo che scriveremo che carichi i titoli:
    		Button btn = (Button)findViewById(R.id.button_carica);
    btn.setOnClickListener(new OnClickListener() {

    @Override
    public void onClick(View v) {
    caricaNotizie();
    }
    });

HttpUrlConnection per leggere dal server

Immagine slide
  • In Android esiste una classe URL che server per lavorare con sorgenti dati che utilizzano vari protocolli
  • La prima operazione che dovremo fare è creare una nuova URL che contenga l'indirizzo, o meglio l'url del nostro feed:
    URL url = new URL("http://www.fermimn.gov.it/?action=rss");
  • Veniamo ora al caricamento dei contenuti.
  • Apriamo ora la connessione HTTP attraverso l'URL:
    HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();

HttpUrlConnection per leggere dal server

Immagine slide
  • Visto che questi due metodi possono generare eccezioni, li inseriremo in un try-block e nella relativa catch ritorneremo il messaggio di errore:
    try {
    ...
    } catch (Exception ee) {
    return ee.getMessage();
    }
  • Dato che ci apprestiamo a leggere un file di testo, ci procueremo una variabile per la linea letta ed uno stream di testo, associato alla connessione:
    String linea;
    BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection.getInputStream()));

HttpUrlConnection per leggere dal server - 2

Immagine slide
  • Dovremo analizzare le righe e scegliere solo quelle con il titolo, una volta scelte, le ripuliremo:
    if (linea.contains("<title>") && !linea.contains("NEWS</")) {
    linea = linea.trim().replaceAll("<?title>", "");
    <aggiungo il titolo al testo>
    }
  • Dovremo analizzare le righe e scegliere solo quelle con il titolo, una volta scelte, le ripuliremo:
    if (linea.contains("<title>") && !linea.contains("NEWS</")) {
    linea = linea.trim().replaceAll("<?title>", "");
    <aggiungo il titolo al testo>
    }

HttpUrlConnection per leggere dal server - 2

Immagine slide
  • La creazione del BufferedReader e la conseguente lettura possono generare un'eccezione, che va gestita, ma anche richiedono la chiusura del BufferReader alla fine.
  • Creremo quindi un blocco try-catch con un finally:
    try {
    ...
    } catch (Exception e) {
    return e.getMessage();
    }
    finally {
    urlConnection.disconnect();
    }

HttpUrlConnection per leggere dal server - 2

Immagine slide
  • Allego per chiarezza il pezzo di codice con la funzione descritta, nel suo complesso

Connessioni al server e interfaccia utente

Immagine slide
  • Se noi richiamiaamo il codice appena scritto direttamente dall'handler del bottone Android (superiore al 4) invia un'eccezione
  • Questo perché tutte le interzioni con il server, per quanto semplici possono richiedere diverso tempo
  • Se l'interazione viene eseguita nel thread dell'interfaccia utente (come ad esempio nell'handler di un bottone), l'interfaccia è bloccata.
  • Non potremo mostrare le informazioni intanto che le leggiamo

Connessioni al server e interfaccia utente

Immagine slide
  • La videata non scrollerà ed i bottoni non funzioneranno
  • Per questo è indispensabile eseguire le interazioni con il server in un thread separato
  • Se però da questo thread vogliamo modificare l'interfaccia, ad esempio aggiungere di mano in mano le righe lette, risulta abbastanza complicato perché ogni modifica dell'interfaccia utente può essere fatta solo dall thread dell'interfacia utente

AsyncTask per gestire connessioni al server

Immagine slide
  • Vista questa difficoltà, Android mette a disposizione una classe per gestire un task in backgrount in un thread.
  • Questa classe mette a disposizione un meccanismo per inviare dati all'interfaccia utente di tanto in tanto
  • Mette anche a disposizione un metodo per eseguire del codice al termine del lavoro, nell'interfaccia utente.
  • Per maggiore flessibilità consente anche di passare dei parametri al task in background.

AsyncTask per gestire connessioni al server

Immagine slide
  • Nel nostro esempio creeremo una classe interna dentro MainActivity derivata da AsyncTask per gestire il caricamento.
  • Utilizzando una classe interna saremo in grado di utilizzare tutti gli attributi ed i metodi della nostra MainActivity all'interno della nuova classe.

I tipi della classe generica AsyncTask

Immagine slide
  • Per essere flessibile al massimo, la classe AsyncTask è una classe generica che consente di scegliere tre diversi tipi.
  • Il primo tipo da scegliere (1) è il tipo del parametro da passare al metodo doInBackground, vale a dire al task.
  • Questo metodo accetta un numero variabile di parametri, ma tutti con il tipo che abbiamo scelto.
  • All'interno del metodo i parametri saranno accessibili tramite l'array params, che è il nome che abbiamo dato al parametro nel metodo.

I tipi della classe generica AsyncTask

Immagine slide
  • Nel nostro caso il parametro non ci serve: l'URL da caricare è predefinito. Ho quindi deciso di scegliere un tipo intero.
  • Il tipo generico non può però essere un tipo scalare, ma deve essere una classe, quindi invece che int uso Integer.
  • Il secondo tipo (2) viene utilizzato per passare il dato periodico. Anche in questo caso avremo un numero variabile di parametri.
  • Nel nostro caso utilizzeremo uno String che conterrà il titolo della prossima news.

I tipi della classe generica AsyncTask

Immagine slide
  • Il terzo tipo (3) è il tipo di ritorno del task, che verrà passato al metodo di terminazione. Il dato passato sarà un'unica istanza.
  • Nel nostro caso utilizzeremo una String, che conterrà il messaggio di errore in caso di eccezione oppure null se l'operazione sarà andata a buon fine.

Metodo doInBackground

Immagine slide
  • Il task è eseguito in un thread in background dal metodo doInBackground.
  • Nel nostro caso il parametro è irrilevante mentre il valore di ritorno sarà una String (eventualmente null).
  • Il lavoro viene svolto dal metodo caricaNotizie che è stato già descritto.
  • È da notare nel ciclo di lettura delle linee che, una volta individuato e formattato un titolo, esso viene passato al metodo publishProgress(linea);

Metodo doInBackground

Immagine slide
  • Questo metodo richiama a sua volta il metodo onProgressUpdate, all'interno del thread dell'interfaccia utente, che gestirà il titolo.
  • Come abbiamo detto, doInBackground ritorna la stringa generata dal metodo caricaNotizie.
  • Questa stringa verrà a sua volta passata come parametro al metodo onPostExecute nel thread dell'interfaccia utente.

Metodo onProgressUpdate e publishProgress

Immagine slide
  • Abbiamo visto che caricaNotizie utilizza publishProgress per passare, come unico parametro, il tiolo letto.
  • Questo titolo finirà dunque nel primo elemento del parametro, progress[0].
  • All'interno del metodo recupereremo il testo già presente nella TextArea:
    String t = notizie.getText().toString();
  • Concateneremo il nuovo titolo, separato da un paio di a-capo:
    t += "nn" + progress[0];
  • E per finire rimetteremo la stringa nella TextView:
    notizie.setText(t);

Metodo onPostExecute

Immagine slide
  • Per finire, il metodo onPostExecute gestirà il termine del task in background.
  • Riceverà come parametro la descrizione dell'eccezione se c'è stata un'eccezione, oppure null se il caricamento è andato a buon fine.
  • Dovremo quindi fare un if che gestisca il caso di errore:
    if (result != null) {
    ...
    }
  • Se c'è stato un errore, avviseremo l'utente con un Toast:
    Toast.makeText(getApplicationContext(), result,
    Toast.LENGTH_SHORT).show();

Metodo onPostExecute

Immagine slide
  • Se c'è stata un'eccezione, le news non saranno state caricate, quindi rimetteremo il testo di default nella TextView:
    notizie.setText(getResources().getString(R.string.text_placeholder));
  • In ogni caso, prima di terminare renderemo invisibile la PrograssBar che copre i titoli:
    attesa.setVisibility(View.INVISIBLE);

Download del codice

[any material that should appear in print but not on the slide]