back to top

Accesso a contenuti remoti con Android Studio

Per un’app un metodo più usuale di comunicare con il mondo esterno è ilprotocollo HTTP, attraverso cui è possibile effettuare una grande varietà di operazioni, come il download di pagine web da un server, il download di dati binari e così via. Nella presente lezione creeremo un’applicazione in cui utilizzando HTTP scaricheremo diversi tipi di contenuti.

Avviamo dunque Android Studio e creiamo un nuovo progetto denominato TestWebService. Apriamo il file AndroidManifest.xml e modifichiamolo aggiungendo la sezione uses-permission come mostrato di seguito

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.test.testwebservice">

    <uses-permission android:name="android.permission.INTERNET"/>

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        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>

Modifichiamo il file MainActivity.java nel modo seguente

package com.example.test.testwebservice;

import android.app.Activity;
import android.os.Bundle;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import android.util.Log;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    private InputStream ApriConnessioneHttp(String urlString) throws IOException
    {
        InputStream in = null;
        int risposta = -1;

        URL url = new URL(urlString);
        URLConnection conn = url.openConnection();

        if (!(conn instanceof HttpURLConnection))
        throw new IOException("No connessione HTTP");

        try{
            HttpURLConnection httpConn = (HttpURLConnection) conn;
            httpConn.setAllowUserInteraction(false);
            httpConn.setInstanceFollowRedirects(true);
            httpConn.setRequestMethod("GET");
            httpConn.connect();
            risposta = httpConn.getResponseCode();
            if (risposta == HttpURLConnection.HTTP_OK) {
                in = httpConn.getInputStream();
            }
        }
        catch (Exception ex)
        {
            Log.d("Connessione", ex.getLocalizedMessage());
            throw new IOException("Errore connessione");
        }

        return in;
    }
}

Analizziamo, come di consueto, il codice appena visto. Poiché vogliamo utilizzare il protocollo HTTP per connetterci al web, la prima cosa di cui la nostra applicazione necessita è il permesso INTERNET che abbiamo impostato nel file AndroidManifest.xml.

Successivamente abbiamo definito all’interno della classe MainActivity il metodo ApriConnessioneHttp, che riceve come parametro un URL e restituisce un oggetto InputStream, utilizzando il quale è possibile scaricare dati leggendo i byte. In questo metodo facciamo uso dell’oggetto HttpURLConnection per aprire una connessione verso un URL remoto, impostando per esso alcune proprietà come il metodo di richiesta (GET)

HttpURLConnection httpConn = (HttpURLConnection) conn;
httpConn.setAllowUserInteraction(false);
httpConn.setInstanceFollowRedirects(true);
httpConn.setRequestMethod("GET");
httpConn.connect();

Dopo aver tentato la connessione con il server, viene restituito il codice HTTP di risposta. Se la connessione viene stabilita (attraverso il codice di risposta HTTP_OK), allora si può procedere ottenendo un oggetto InputStream

if (risposta == HttpURLConnection.HTTP_OK) {
    in = httpConn.getInputStream();
}

Una delle attività più comuni all’interno di un’applicazione è il download di dati binari dal web, come immagini. Adesso vedremo come scaricare un’immagine dal web e visualizzarla all’interno di una nostra applicazione. Utilizziamo il progetto TestWebService e andiamo a modificare il file activity_main.xml aggiungendo un ImageView

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
<ImageView
android:id="@+id/img"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center" />
    </LinearLayout>

Adesso andiamo a modificare il file MainActivity.java andando ad inserire i seguenti package

import android.widget.ImageView;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;

<p>
Inseriamo nella classe i seguenti due metodi
</p>

private Bitmap ScaricaImmagine(String URL)
{
    Bitmap bitmap = null;
    InputStream in = null;
    try {
        in = ApriConnessioneHttp(URL);
        bitmap = BitmapFactory.decodeStream(in);
        in.close();
    } catch (IOException e1) {
        Log.d("Servizio web", e1.getLocalizedMessage());
    }
    return bitmap;
}

private class ScaricaImmagineTask extends AsyncTask<String, Void, Bitmap> {
    protected Bitmap doInBackground(String... urls) {
        return ScaricaImmagine(urls[0]);
    }
    protected void onPostExecute(Bitmap result) {
        ImageView img = (ImageView) findViewById(R.id.img);
        img.setImageBitmap(result);
    }
}

Infine modifichiamo il metodo onCreate nel modo seguente

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  new ScaricaImmagineTask ().execute(
  "http://dc942d419843af05523b-ff74ae13537a01be6cfec5927837dcfe.r14.cf1.rackcdn.com/wp-content/uploads/Android-logo.png");
}

Chiaramente come indirizzo HTTP potete inserire qualsiasi indirizzo che punti ad un’immagine.

Avviamo l’applicazione sull’emulatore, il risultato dovrebbe essere simile al seguente

Il metodo ScaricaImmagine riceve come parametro l’URL dell’immagine da scaricare e apre la connessione al server utilizzando il metodo ApriConnessioneHttp che abbiamo definito in precedenza. Utilizzando l’oggetto InputStream restitutito dalla connessione, viene utilizzato il metodo decodeStream della classe BitmapFactory per scaricare e decodificare i dati in un oggetto Bitmap, infatti il metodo ScaricaImmagine restituisce un oggetto Bitmap.

Una cosa importante da evidenziare è che a partire dalla versione 3.0 di Android le operazioni sincrone non possono essere eseguite direttamente da un thread dell’interfaccia utente. Se proviamo a chiamare il metodo ScaricaImmagine direttamente all’interno del metodo onCreate la nostra applicazione andrà in errore.

Questo succede perché il metodo ScaricaImmagine è sincrono e ciò significa che esso non restituisce il controllo prima di aver terminato la sua operazione, cioè lo scaricamento dell’immagine. A partire dalla versione 3.0 di Android tutto il codice sincrono deve essere gestito tramite la classe AsyncTask, che consente di effettuare operazioni in background in thread separati e poi restituire il risultato al thread dell’interfaccia utente.

Per tale motivo la chiamata al metodo ScaricaImmagine avviene tramite la classe privata ScaricaImmagineTask

private class ScaricaImmagineTask extends AsyncTask<String, Void, Bitmap> {
    protected Bitmap doInBackground(String... urls) {
        return ScaricaImmagine(urls[0]);
    }
    protected void onPostExecute(Bitmap result) {
        ImageView img = (ImageView) findViewById(R.id.img);
        img.setImageBitmap(result);
    }
}

In questa classe tutto il codice da eseguire in modo asincrono deve essere inserito nel metodo doInBackground. Quando l’operazione termina il risultato viene restituito tramite il metodo onPostExecute. Per richiamare questa classe basta creare un’istanza della stessa e chiamare il suo metodo execute.

Lo scaricamento di dati binari è solo una delle attività che è possibile eseguire tramite il web. Un’altra attività comune è quella di scaricare porzioni di dati testuali. Per farlo basta seguire l’esempio precedente importando il package

import java.io.InputStreamReader;

e inserendo nella classe MainActivity il seguente codice

private String ScaricaTesto(String URL)
{
    int BUFFER_SIZE = 2000;
    InputStream in = null;
    try {
        in = ApriConnessioneHttp(URL);
    } catch (IOException e) {
        Log.d("Servizio web", e.getLocalizedMessage());
        return "";
    }
    InputStreamReader isr = new InputStreamReader(in);
    int charRead;
    String str = "";
    char[] inputBuffer = new char[BUFFER_SIZE];
    try {
        while ((charRead = isr.read(inputBuffer))>0) {
            String readString = String.copyValueOf(inputBuffer, 0, charRead);
            str += readString;
            inputBuffer = new char[BUFFER_SIZE];
        }
        in.close();
    } catch (IOException e) {
        Log.d("Servizio web", e.getLocalizedMessage());
        return "";
    }
    return str;
}

private class ScaricaTestoTask extends AsyncTask<String, Void, String> {
    protected String doInBackground(String... urls) {
        return ScaricaTesto(urls[0]);
    }
    @Override
    protected void onPostExecute(String result) {
        Toast.makeText(getBaseContext(), result, Toast.LENGTH_LONG).show();
    }
}

Infine nel metodo onCreate basta effettuare la seguente chiamata

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);

    new ScaricaTesto Task().execute("http://...");
}
Pubblicitร