lunedì 16 gennaio 2012

Programmazione Android: Costruire una web application (prima parte)

Grazie al regalo di Natale di mia moglie (un tablet Acer Iconia Tab A500), ultimamente mi sono interessato alla programmazione di applicazioni Android. questo è il primo di una serie di post in cui affronterò l'ambiente android ed in particolare lo sviluppo di applicazioni Web per android, un misto tra applicazioni android ed applicazioni Web. Tralascio volutamente tutta la parte relativa alla preparazione dell'ambiente di sviluppo, ampiamente disponibile via web; l'unica cosa a cui accenno è che è necessario fornirsi di:

Preparato l'ambiente, Eclipse è ora in grado di gestire al meglio i progetti Android. Per iniziare un nuovo progetto, File ->New->Project e selezioniamo Android Project.
Quindi diamo un nome al progetto (ad esempio, webapp) e selezioniamo la versione dell'sdk di riferimento. I tablet in commercio attualmente sono equipaggiati della versione 3.2 (Honeycomb), ma molti telefoni hanno ancora la versione 2.3, ragion per cui io scelgo di sviluppare con una versione meno recente, come si vede in figura.
Successivamente verrà chiesto il package, è norma utilizzare tre parole separate dal punto ".", ad esempio net.stefanobianchini.applicazioneweb. Lo scheletro del progetto è quindi pronto.

L'icona
Una applicazione che si rispetti ha bisogno di un'icona giusta. Cercate un'icona 72x72 pixel in formato png che più rappresenta la vostra applicazione e posizionatela sotto res/drawable-hdpi (qui una spiegazione sulle directory delle risorse grafiche), chiamandola ad esempio ic_webapp.png. A questo punto si apre il file AndroidManifest.xml e nella sezione application->application attributes->icon impostare @drawable/ic_webapp. Questa configurazione chiederà ad android di utilizzare l'immagine che avete scelto come icona nel menù principale, una volta che l'applicazione sarà installata. La modifica è simile anche se si sceglie di modificare direttamente l'xml anziché utilizzare il wizard dell'ADT.

I permessi
Fondamentale per lo sviluppo Android è la corretta gestione dei permessi. Una applicazione ha bisogno di permessi particolari, infatti, per collegarsi ad internet, usare il GPS, la fotocamera eccetera. Il consenso al rilascio di questi permessi viene effettuato al momento dell'installazione da parte dell'utente. Ma dobbiamo spiegare ad Android di quali permessi ha bisogno la nostra applicazione! Nel nostro caso, solo dei permessi della navigazione internet. Apriamo quindi il manifest (AndroidManifest.xml) e cerchiamo la parte "Permission": cliccando su "Add" si selezione "Uses Permission" e poi "Android.Permission.Internet". Tutto qua, niente di trascendentale.

Vediamo quindi il risultato xml di queste modifiche:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.stefanobianchini.webapp"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="10" />
    <uses-permission android:name="android.permission.INTERNET"/>
    <application
        android:icon="@drawable/ic_webapp"
        android:label="@string/app_name" >
        <activity
            android:name=".webappActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

Come si vede dall'xml, il nome che sarà visualizzato nel menù di android che identificherà l'applicazione è un valore chiamato app_name all'interno delle risorse stringa del progetto. Le risorse stringa sono simili alle risorse grafiche (drawable), è praticamente un file xml con coppie di nomi - valori. Se apriamo il file res->values->strings.xml possiamo modificare il valore della stringa app_name, in modo da personalizzarla:

Il Layout
Android predilige il modello MVC, quindi il layout sarà separato dalla parte del funzionamento dell'applicazione. E cosa meglio di XML per disegnare i vari componenti grafici dell'applicazione? Ovviamente si può anche sfruttare l'editor visuale di Eclipse che dà una bella mano. La parte grafica si trova sotto res->layout e poiché Eclipse già crea nel progetto base una impaginazione "Hello World", basterà modificare il file main.xml già presente. Nel nostro caso è molto, molto semplice: eliminiamo la textview esistente ed andiamo ad inserire un componente magico, chiamato WebView (sotto la linguetta Composite).
Un'occhiata al risultato xml:
<?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" >

    <WebView
        android:id="@+id/webView1"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>
Come si vede dalla figura, Eclipse ha chiamato il componente appena inserito "webView1". Ovviamente il nome può essere cambiato.

Il codice sorgente
Ora che tutto è pronto, diamo una occhiata al comportamento che l'applicazione deve adottare. Apriamo il file creato automaticamente webappActivity.java (ovviamente, se il progetto si chiama pincopallino il file che eclipse creerà automaticamente sarà nominato pincopallinoActivity.java). Iniziamo dalla base: appena l'applicazione viene lanciata, viene eseguito un metodo standard chiamato onCreate() dell'attività scelta per essere la principale (ossia quella eseguita per prima). Questo metodo viene reimplementato (override) e possiamo inserire dentro tutte le istruzioni utili al funzionamento base dell'applicazione, non prima però di aver definito una variabile pubblica relativa all'oggetto webview:
public WebView webView1;
/** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //istruzione che imposta il layout main.xml
        setContentView(R.layout.main);      
        //ottengo l'oggetto webview
        webView1 = (WebView) findViewById(R.id.webView1);
        //Attivo il supporto javascript
        webView1.getSettings().setJavaScriptEnabled(true);
        //Carico la pagina desiderata
        webView1.loadUrl("http://www.stefanobianchini.net");
    }
E la prima funzione di base è pronta: ora però vogliamo che, navigando tra le pagine, la pressione del pulsante indietro del sistema android permetta di navigare nella cronologia anziché tornare al menù principale. Questo può essere fatto attraverso un override semplice:
@Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_BACK) && webView1.canGoBack()) {
         webView1.goBack();
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }
Bene. L'applicazione ha già la sua funzionalità di base, come si comprende se viene eseguito il progetto da Eclipse nell'emulatore. Se però si carica l'applicazione su un device, aprendo e chiudendo l'applicazione si possono avere dei problemi: questo perché l'applicazione, quando si torna al menù principale del sistema, viene messa in pausa automaticamente dall'OS Android. Per risolvere questo problema bastano poche righe di codice:
public void onResume() {
       super.onResume();
       if (webView1 != null) {
        webView1.resumeTimers();
       }
     }

    public void onPause() {
       super.onPause();
       if (webView1 != null) {
        webView1.pauseTimers();
       }
     }

Ora l'applicazione è funzionante. Nei futuri post spiegherò come utilizzare un framework per lo sviluppo di applicazioni web mobile (jquery mobile), ossia simulare una applicazione vera e propria via internet. Inoltre verranno integrate piccole migliorie all'applicazione base mostrata in questo post, come ad esempio un pulsante esci, un pulsante home, una finestra di caricamento e magia delle magie, un ponte che collega javascript al codice java della nostra applicazione android, ovvero come eseguire comandi java dal una pagina web per android. Questo ovviamente apre vasti scenari (utlizzo della fotocamera, GPS eccetera). Ovviamente con i permessi opportuni :-)

15 commenti:

Anonimo ha detto...

Ciao scusami, la tua guida è chiarissima però in eclipse ho degli errori come : setContentView(R.layout.main); (main cannot be resolved or is not a field).
come devo fare? grazie mille

Unknown ha detto...

Ciao, è capitato spesso anche a me, è un problema di eclipse nel copia e incolla (in particolare nella sezione degli import). A questo indirizzo ti spiegano come ripristinare il tutto (eliminando "import android.R" che eclipse mette dopo un copia e incolla)
http://stackoverflow.com/questions/885009/r-cannot-be-resolved-android-error

Denzel ha detto...

Salve signor Bianchini!
Volevo proporle una collaborazione! mi può contattare ad admin@makerando.com ?

Anonimo ha detto...

Bella guida complimenti.
Ho pero' due perplessita'.

Non hai indicato come dichiarare la variabile globale e aprendo i link dall'url chiamato nell'applicazione si apre una nuova pagina del browser.
Sai come si fa'rimanere all'interno dell'applicazione?

Anonimo ha detto...

Salve Stefano, le chiedo se ha mai sentito parlare del linguaggio Human Language Code! Grazie!

Unknown ha detto...

Giusta osservazione. Per gestire i link all'interno dell'applicazione senza aprire un browser, o comunque decidere quali link debbano rimanere all'interno dell'applicazione e quali no, bisogna creare una classe che estenda WebViewClient ed associarlo all'oggetto webView. Per intenderci, esattamente il codice che si può trovare qui http://pastebin.com/CeQZ30NB

Unknown ha detto...

Sono spiacente ma non conosco il linguaggio "Human Language Code"... sono alquanto ignorante in materia :-)

Giovanni ha detto...

Ottima guida davvero utile, solo che c'è un errore. Mi sto avvicinando adesso alla programmazione per Android anche se studio Java da un anno.
Come qualsiasi programma Java ogni variabile va dichiarata ed inzializzata.
Nel mainactivity devi usare così:
WebView webView1 = (WebView) findViewById(R.id.webView1);

Giovanni ha detto...

Sono sempre io, altro errore, setContentView(R.layout.main);

Al posto di main devi mettere il nome del file xml del layout. Eclipse utilizza activity_main

Unknown ha detto...

@Giovanni:
Giusta l'osservazione sulla dichiarazione della webview, l'ho omesso perché Eclipse dichiara l'oggetto webView in automatico dal composer grafico dell'interfaccia (e genera automaticamente la dichiarazione). Ho modificato il codice aggiungendo la stessa (public WebView webView1;) così effettivamente è più completo come esempio.

Per l'altra osservazione, invece, nell'eclipse che uso io la direttiva setContentView(R.layout.main); funziona, poiché il file xml si chiama main. Se leggi nella guida ho scritto "[...] poiché Eclipse già crea nel progetto base una impaginazione "Hello World", basterà modificare il file main.xml già presente [...]"

E' possibile che nelle nuove versioni Eclipse possa chiamare l'xml del layout di default in modo differente, quindi hai fatto bene a segnalarlo.

Unknown ha detto...

Ciao Andrea, puoi usare questo trick aggiungendolo al codice del mio post:
webView1.setWebViewClient(new WebViewClient(){
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
In questo modo sovrascrivi il comportamento standard facendo aprire tutti i link all'interno della app :-)

Giuseppe ha detto...

Ciao Stefano e grazie per le tue utilissime guide!
La ciliegina sulla torta sarebbe poter aggiungere una barra (o una gif) durante il caricamento, per evitare che l'utente possa pensare che l'app si sia bloccata.
Ho provato in alcuni modi indicati da altri in rete ma non riesco. Hai qualche idea?

Unknown ha detto...

Ciao Giuseppe, la soluzione migliore sarebbe:
- usare un framework "responsive", ad esempio negli ultimi tempi io mi trovo molto bene con Bootstrap;
- cercare di effettuare i cambi pagina attraverso chiamate ajax anziché da url diretto (JQuery Load)
- utilizzare la funzione .ajaxStart di jQuery per mostrare una gif animata di caricamento (.ajaxStart) fino all'avvenuto caricamento della pagina (.ajaxStop)

Anonimo ha detto...

Ciao Stefano,

grazie per questo articolo!

Non ho ancora avuto modo di provarlo perché devo prima completare la piattaforma su web. Ma credo mi darà una grande mano.

Una domanda:
Hai per caso affrontato in seguito a questo articolo anche la creazione di una webapp per iOS (se non anche dei più recenti Firefox e Windows)?

Grazie ancora.

Unknown ha detto...

Scusa il terribile ritardo.
Purtroppo no, non ho le conoscenze per lo sviluppo di una webapp in iOS.
Mi permetto però di consigliarti Phonegap, un framework multi piattaforma che ti permette di sviluppare webapp standard per svariate piattaforme!