I blog di Alessioempoli

Data 13 ottobre 2018

App Java per Android: librerie per ogni esigenza

Per ingrandire il testo, cliccare sul browser

Per ingrandire le foto, cliccarci sopra

  App Java per Android: librerie per ogni esigenza

 

 

Un’introduzione alle migliori librerie Open Source scritte in Java per affrontare alcune tra le problematiche più frequenti nella creazione di App per Android.

Nel normale sviluppo di un’applicazione, a prescindere dalla piattaforma target, è facile ritrovarsi ad affrontare le medesime tematiche come ad esempio l’identificazione dell’utente, la persistenza dei dati in locale o l’accesso a servizi Web.

Il Software Developer Kit di Android, oltre ad offrire le API fondamentali per qualunque applicazione, espone API e componenti atti a risolvere svariate problematiche. Per le tematiche non “affrontate” dall’SDK, vengono normalmente in aiuto le librerie di terze parti.

La piattaforma del Robottino Verde è una delle più interessante, è quindi ovvio disporre di una miriade di librerie mutuate dal mondo Java o realizzate proprio per Android.

 

Lo scopo di questa guida è presentare alcune librerie Open Source in Java e scritte per affrontare alcune tra le problematiche di sviluppo più frequenti.

Per poter trarre benefici dalla lettura di questa guida sono necessari:

 

# buona conoscenza del linguaggio Java.

# I concetti base dello sviluppo su Android (si darà per scontato che il lettore conosca il funzionamento di Activity, Fragment, Intent, Service e così via).

# Conoscenza dell’ambiente di sviluppo Android Studio.

# Conoscenza dell’ambiente di sviluppo Eclipse.

 

Roadmap

Esistono diverse librerie oltre a quelle che si andranno ad analizzare nella guida. La scelta delle librerie è dettata dalla mia personale esperienza nello sviluppo software su piattaforma Android.

Per ogni libreria verrà prima introdotto il problema che gestisce e subito dopo vedremo come la libreria lo affronta:

 

# Binding dei widget della user interface: il collegamento tra widget presenti su un layout ed il relativo componente sottostante (activity o fragment) è un operazione tediosa che facilmente introduce fastidiosi bug. Per rendere meno gravosa questa attività utilizzeremo ButterKnife.

# Caricamento delle immagini: la visualizzazione di immagini è un altro task che il developer è tenuto spesso a dover affrontare. In alcuni casi le immagini sono già presenti in modo statico nell’applicazione, ma a volte è necessario utilizzare la connessione di rete per recuperare da qualche server remoto le immagini necessarie. Vedremo come recuperare e visualizzare le immagini con Glide.

# Comunicazione tra componenti: uno degli aspetti più delicati nella progettazione e realizzazione di applicazioni su piattaforma Android è proprio la definizione delle iterazioni tra Activity, Fragment e Service. L’uso degli Intent è sufficientemente semplice, ma all’ingrandirsi delle dimensioni dell’ applicazione si corre il rischio di perdere la “bussola”. Per questa tematica utilizzeremo EventBus.

# Persistenza dei dati: le principali modalità per rendere persistente un dato in locale consiste nell’uso di file, di una base dati SQLite o delle Shared Preferences. Affronteremo la persistenza con la libreria Kripton.

# Utilizzo di servizi WEB: su piattaforma mobile i servizi SOAP non vengono praticamente utilizzati, quindi parleremo esclusivamente di servizi REST. Tipicamente, nel caso in cui lato back end siano presenti servizi SOAP, si utilizzano sistemi o sottosistemi bridge al fine di convertire i servizi SOAP in REST e viceversa. I servizi REST si basano su HTTP e sul formato dati JSON. In questa parte della guida vedremo la libreria Retrofit.

# Autenticazione: nei progetti con la necessità di salvare dati associati all’utente, una delle prime necessità è quella di dover identificare quest’ultimo. Capita molto spesso di appoggiarsi ad Identity Provider ben noti quali ad esempio Facebook, Twitter e Google+. Affronteremo questa tematica con Scribe.

# Gestione dei permessi: chi ha già sviluppato avendo come target Android 6 “Marshmallow” o superiore si sarà sicuramente scontrato con l’esigenza di far chiedere all’applicazione i permessi necessari al funzionamento in runtime. Affronteremo questo tema con PermissionsDispatcher.

 

L’insieme delle librerie che verrà presentato è costituito esclusivamente da soluzioni Open Source i cui sorgenti sono disponibili su GitHub. Tali librerie possono essere utilizzate insieme senza incompatibilità.

La presentazione delle varie librerie sarà accompagnata da piccoli progetti d’esempio.

 

 

Butterknife ed il binding dei componenti visuali

 

Analizziamo la libreria Butterknife e la sua utilità nel binding dei componenti visuali durante lo sviluppo di applicazioni Java per Android.

 

Il problema

Prima di presentare la libreria è bene fare un breve cappello introduttivo utile a descrivere il contesto e le motivazioni che hanno spinto alla realizzazione di ButterKnife. Nello sviluppo su piattaforma Android, la definizione dell’interfaccia si divide normalmente in due parti: una definizione del layout su uno o più file xml, ed un backend scritto in Java che si occupa di controllare i comportamenti dell’interfaccia stessa nonché della sua interazione con il resto dell’applicazione.

Il cosidetto binding tra layout xml e componenti consente di pilotare il layout delle componenti e gli eventi ad essi associate direttamente dalle classi Java. Prendiamo ad esempio l’activity riportata in Figura 1:

 

1-

La sua definizione sottoforma di layout xml è:

 

2-

Mentre la activity è così definita:

 

3-

 

Con il metodo findViewById l’activity recupera le view, è quindi possibile definire i listener per i vari eventi o modificarne gli attributi. Il codice sopra riportato consente di intravedere il problema: all’aumentare della complessità dell’interfaccia, e di conseguenza dei suoi componenti, l’activity o il fragment associato diventano una lunga sequenza di richiami di findViewBy per recuperare i componenti visuali e, subito dopo, una lunga sequenza di definizione di listener associati ai vari eventi. Il codice alla lunga diventa poco comprensibile.

 

 

Binding delle view con Butterknife

 

Butterknife è una libreria open source realizzata per rendere più semplice il collegamento tra la parte xml del layout e la sua controparte Java. Si basa sull’uso di annotazioni e di un processore di annotazioni che in fase di compilazione provvede a generare il codice in grado svolgere questo compito per noi. Rivediamo ora l’esempio precedente con l’uso di Butterknife:

 

4-

 

Il binding con ButterKnife avviene mediante le annotazioni @BindView per le view e @OnClick per l’evento click del pulsante btnPremi. Il processore di annotazione provvede a generare il codice necessario alla gestione del binding. Con il metodo ButterKnife.bind(this) viene effettuato il collegamento tra le view ed i relativi attributi nella classe activity main

 

 

Butterknife: un esempio pratico

 

Esempio pratico dell’utilizzo di ButterKnife: binding multiplo degli eventi, uso con i recycler view e setup.

 

Butterknife: Binding multiplo degli eventi

Nell’esempio precedente, si era creato il metodo submit per gestire l’evento onClick del pulsante btnPremi. Lo stesso metodo può essere usato per gestire contemporaneamente l’evento di più controlli:

 

5-

Il parametro view del metodo viene usato per passare come argomento del metodo la view che ha generato l’evento.

 

Binding opzionale

Supponiamo ora di avere una variante del layout visto in precedenza con un ulteriore pulsante denominato btnPremi2.

 

6-

 

Se lasciassimo tutto inalterato, la prima volta che si accede alla versione portrait del layout ci troveremmo innanzi ad un’eccezione generata dalla libreria ButterKnife stessa. Per ovviare a questo problema, basta decorare l’attributo con l’annotazione @Nullable ed il metodo per la gestione dell’evento con l’annotazione @Optional. Il codice fin qui descritto diventerebbe:

 

7-

 

ButterKnife: binding su fragment

Il binding su fragment avviene nello stesso modo in cui avviene quello con le activity, tenuto conto delle note differenze tra il lifecycle delle activity e quello dei fragment.

 

8-

 

ButterKnife: uso con i recycler view

L’ultima caratteristica di ButterKnife che andiamo a vedere è la sua capacità di funzionare anche gli adapter. Vediamo ora come creare un’activity che contenga un recycler view che utilizza ButterKnife e che visualizzi un semplice elenco di contatti telefonici:

 

9-

 

Il codice del layout non presenta particolari peculiarità, quindi riporteremo solo la parte di codice relativa all’integrazione tra l’adapter e ButterKnife. Si rimanda al codice allegato all’articolo su ButterKnife per visionare il codice completo.

La cosa interessante è come viene definito il ViewHolder dell’adapter del recycler view.

 

10-

 

Il codice sopra riportato illustra come, all’interno dell’adapter del recycler view, venga utilizzato ButterKnife per effettuare il collegamento tra le view definite nel layout dell’item del recycler view e gli attributi del view holder.

 

Butterknife: setup

La configurazione di ButterKnife in un progetto Android consiste nella dichiarazione del suo processore di annotazione e della libreria che serve a runtime.

 

11-

ButterKnife è disponibile come progetto open source su github al seguente link. La documentazione ufficile è invece disponibile qui. Nell’allegato troverete il codice completo illustrato in questa parte della guida.

 

 

Glide e il caricamento delle immagini

 

Impariamo ad utilizzare Glide, una libreria open source scritta in Java per Android che si occupa di caricare immagini in modo efficiente.

 

Il problema

Nell’esempio della sezione precedente le immagini utilizzate nel recycler view sono contenute nell’app tra le risorse drawable. Molto spesso ci si trova a dover visualizzare liste con immagini provenienti da fonti esterne alle risorse dell’app. Immagini provenienti dal Web o dal file system sono i tipici esempi.

 

Quando si carica un’immagine bisogna sempre tenere in considerazione che il tempo di recupero dello stessa può essere significativo. Al fine di evitare vistosi rallentamenti nell’interfaccia utente, soprattutto quando si utilizza le connessioni di rete per recuperare le risorse, è bene effettuare queste operazioni su un altro thread (le connessioni di rete sul thread principale sono normalmente vietate, proprio per questo motivo).

Gestire in modo asincrono il caricamento delle immagini può essere un task di difficile gestione e un facile generatore di bug.

 

Glide: caricamento delle immagini

Glide è una libreria open source scritta in Java per piattaforma Android che si occupa di una sola cosa: caricare immagini in modo efficiente. Per illustrare il suo funzionamento prenderemo l’esempio della sezione precedente in cui si utilizzava un recycler view per visualizzare un elenco di contatti e lo arricchiremo con delle immagini prese dal sito Loremflickr.

Per utilizzare Glide all’interno di un progetto e includere la libreria dovremo operare nel seguente modo:

 

12-

Nel manifest dell’applicazione deve essere presente il permesso di utilizzare la connessione ad Internet.

 

13-

 

Per caricare contenuti nelle image view utilizzate all’interno del recycler view è sufficiente modificare il codice del recycler view adapter nel seguente modo:

 

14-

Il codice che consente di caricare le immagini in modo asincrono per poi visualizzarlo nell’image view denominata imageView è in particolare:

 

15-

Con il codice sopra riportato abbiamo la possibilità di caricare le immagini in modo asincrono. E se volessimo personalizzare il caricamento delle immagini? Glide consente di fare ciò mediante un oggetto di tipo RequestOptions. Nel nostro caso vogliamo inserire un place holder (la risorsa ic_contact_phone_black_24dp) in attesa del caricamento e trasformare l’immagine caricata in un cerchio. Per fare questo, è sufficiente modificare il codice precedente in:

 

16-

Il risultato ottenuto è simile a quanto riportato nell’immagine:.

 

17-

Eseguendo l’applicazione in allegato a noterete che l’elenco dei contatti viene caricato immediatamente, mentre le immagini vengono visualizzate in modo asincrono senza bloccare la UI dell’applicazione.

Glide è disponibile come progetto open source su GitHub. La documentazione ufficiale è invece disponibile qui.

 

 

EventBus e la comunicazione tra componenti

 

Un’introduzione a EventBus che consente di implementare un sistema di comunicazione tra componenti basati su eventi da collegare per emettere eventi o sottoscrivere messaggi.

 

Il contesto

Quando si parla di sviluppo su piattaforma Android si parla tipicamente di realizzare applicazioni basate su componenti quali Activity, Fragment, Service ed Intent. I componenti appena citati, tranne i service, gestiscono l’interfaccia utente e lavorano sul thread main, ovvero il thread preposto alla gestione della user interface o UI.

Affrontiamo ora una situazione tipica: a fronte di un input da parte dell’utente, l’applicazione avvia un’attività onerosa in termini di tempo e risorse mediante un thread in background. Al termine dell’operazione, si desidera che il termine dell’operazione venga segnalato mediante una notifica visiva. L’esempio più classico di tale situazione è quella in cui l’applicazione deve effettuare il download di risorse dal Web.

 

18-

Il sequence diagram appena illustrato cerca di riassumere graficamente quanto spiegato. Da notare che il service e l’activity vengono gestiti da thread diversi. L’activity avvia il service mediante degli intent. Quando il servizio in background termina, notifica l’avvenuta conclusione dei lavori mediante un altro intent che viene intercettato dall’activity mediante un broadcast receiver.

Nell’activity l’avvio del servizio in background avviene mediante il codice:

 

19-

Sempre nell’activity la registrazione del broadcast receiver:

 

20-

 

Lato service il codice per notificare l’avvenuta fine dell’operazione in background:

 

21-

 

L’esempio basato su Intent e BroadcastReceiver è abbastanza semplice. E’ facile immaginare che all’aumentare della complessità dell’applicazione questo tipo di comunicazione inizi a rendere il codice e la struttura dei componenti poco leggibile e di conseguenza difficile da mantenere.

 

EventBus: come semplificare la comunicazione tra componenti

Un modo per semplificare la comunicazione tra componenti è quello di utilizzare Event bus, una libreria Java open source creata proprio per realizzare un bus di eventi a cui i vari componenti posso “agganciarsi” per ricevere e spedire eventi. Anche questa libreria si basa sull’uso di annotazioni e su un processore di annotazione per generare il codice a supporto dell’applicazione.

 

22-

EventBus consente di implementare un sistema di comunicazione tra componenti basati su eventi a cui questi ultimi si possono collegare per “emettere” eventi o per “sottoscrivere” messaggi. Sul sistema a bus possono esistere diversi tipi di eventi definiti semplicemente da una classe.

 

 

EventBus e gestione degli eventi

 

Analizziamo la fase relativa alla gestione degli eventi di EventBus, libreria che consente di implementare un sistema di comunicazione tra componenti basati su eventi da collegare per emettere eventi o sottoscrivere messaggi.

Nel contesto EventBus un evento è definito da un semplice POJO, ad esempio:

 

23-

Un componente, Activity o Service che sia, per emettere un evento di tipo ActivityEvent deve utilizzare l’istanza del bus per effettuare il post di un evento passandogli come parametri un’istanza del tipo di evento che vuole lanciare.

 

24-

Un componente, per mettersi in ascolto dell’evento rappresentato dalla classe ActivityEvent dovrà esporre un metodo annotato con l’annotazione @Subscribe

 

25-

Ci possono essere diversi sottoscrittori o subscriber per un particolare tipo di evento, come ci possono essere diversi emettitori per lo stesso evento. EventBus consente di specificare su quale thread far rimanere i vari subscriber. Tutto ciò è possibile grazie all’attributo threadMode di @Subscribe.

I possibili valori che tale attributo può assumere sono:

 

26-

 

EventBus: setup

 

Per configurare EventBus su gradle è sufficiente definire nel file build.gradle del progetto le dipendenze del processore di annotazione con la sua configurazione (vedi annotationProcessorOptions).

 

27-

Oltre al processore di annotazione e alla libreria in sé, nel file gradle è necessario definire il nome della classe che verrà generata e nel corso dell’applicazione verrà utilizzata come registro degli eventi e dei subscriber. Nel caso riportato la classe indice sarà it.html.android.libraries.myapplication6.EventBusIndex.

Questa classe indice deve essere registrata su EventBus prima di qualunque altra operazione sul bus. Per questo motivo, questa opearazione viene svolta nell’application, all’avvio dell’app:

 

28-

 

EventBus: registrare componenti sul bus di eventi

 

Un componente activity, service o fragment, per potersi registrare per ricevere la notifica degli eventi, si deve registrare sul bus quando viene creato e, per evitare spreco di risorse, quando viene distrutto deve de-registrarsi.

I vari componenti hanno diversi lifecycle, ma sostanzialmente hanno tutti un metodo eseguito quando viene creato (o attivato) e uno quando viene distrutto (o deattivato).

Nel caso di activity:

 

29-

 

 

EventBus: esempio d’utilizzo

 

Un esempio pratico di utilizzo della libreria EventBus per la realizzazione di applicazioni Android basate su Java.

Per illustrare come funziona event bus, riprendiamo l’esempio visto nella sezione precedente e ripensiamolo con l’uso del bus di eventi:

 

30-

 

Come si vede dal sequence diagram, è stata introdotta un’altra line: il bus di eventi chiamato EventBus. A questo punto è possibile fare alcune considerazioni:

 

# i componenti sono disaccoppiati: nell’esempio iniziale l’activity MainActivity sapeva che doveva invocare il service MyService. Con il bus di eventi l’activity segnala al bus di eventi, e il service riceve l’evento dal bus di eventi. Non c’è una comunicazione diretta tra i due componenti.

# La gestione della comunicazione tra componenti, in termini di codice da scrivere, è più semplice.

# Un evento può essere propagato a più componenti semplicemente aggiungendo dei subscriber con l’annotazione @Subscribe. Se ad esempio si volesse introdurre un altro service chiamato SecondService per svolgere un’altra attività come conseguenza dell’emissione di un evento di tipo Task start, il sequence diagram sopra illustrato diventerebbe:

 

31-

Nell’activity per generare un evento di tipo ActivityEvent è sufficiente eseguire il seguente codice:

 

32-

 

Da notare che alla fine della sottoscrizione dell’evento, viene inviato un altro messaggio di tipo MessageEvent con un codice molto simile a quanto già visto in precedenza.

 

EventBus: sticky events

 

Una caratteristica che e’ bene citare di EventBus è la possibilita’ di poter gestire i cosidetti Sticky Events, ovvero degli eventi che rimangono nella coda degli eventi anche dopo esser stati inviati. A dire il vero viene tenuto solo l’ultimo evento di un determinato tipo.

Grazie a questi tipi di eventi e’ possibile fare in modo che tutti i subscriber ricevano, una volta registrati per un particolare Sticky Event, l’ultimo evento di quel tipo. L’emissione di tali messaggi e’ molto simile a quella di un qualunque messaggio, tranne per il fatto che vengono innescati mediante il metodo postSticky

 

33-

Conclusioni

Al fine di evitare inutili grattacapi nell’uso di EventBus: una volta inserita la libreria nel proprio progetto, se si prova ad eseguire la compilazione del progetto e non ci sono subscriber verrà generato un errore. Ovviamente questo è un finto problema: dopo l’inserimento del primo subscriber l’errore scomparirà. EventBus è disponibile come progetto open source su Github.

 

 

Kripton Persistence Library

 

Analizziamo il funzionamento della Kripton Persistence Library, una libreria per la risoluzione dei problemi di persistenza dei dati.

 

Persistenza su Android

Su Android esistono diversi modi per rendere persistenti i dati dell’applicazione. Essi possono essere suddivisi nelle seguenti categorie:

 

34-

 

Molte applicazioni hanno inoltre l’esigenza di recuperare informazioni da sistemi remoti mediante servizi REST, quindi pur non rientrando direttamente nella tematica della persistenza, la necessità di utilizzare servizi REST è un tema in qualche modo collegato.

Esistono sistemi che consentono di salvare i dati anche on Cloud. In questa categoria troviamo soluzioni come firebase che è una soluzione in grado di salvare i dati sia in locale che on Cloud sui server di Google.

In questa guida non ci occuperemo delle soluzioni on Cloud ma affronteremo la tematica della persistenza on device, nella quale l’informazione viene memorizzata sul dispositivo.

Kripton Persistence Library è un libreria scritta in java che sfrutta un processore di annotazione per generare il codice necessario a rendere persistente gli oggetti Java nelle diverse modalità di persistenza elencate in precedenza.

Le annotazioni contenute in Kripton consentono di “pilotare” la persistenza degli oggetti Java. In questa sezione della guida vedremo i vari tipi di persistenza e come Kripton ci consente di gestirli agevolmente.

Per essere trasparenti, l’autore di questa guida è anche l’autore di Kripton Persistence Library. Il motivo per cui si è realizzato questa libreria è presto detto: esistono diverse librerie per la persistenza su file system. Esistono diverse librerie che si occupano della persistenza su SQLite. Esistono alcune librerie che si occupano della persistenza mediante Shared Preference. Non esisteva però alcuna libreria che affrontasse tutte e tre le tipologie di persistenza assieme, in modo uniforme e coerente, e che puntasse alle performance grazie all’uso dei processori di annotazione.

 

L’idea alla base del funzionamento di Kripton è semplice: si decorano oggetti di tipo Plain Old Java Object (POJO) e i loro attributi con delle annotazioni. In base alle annotazioni usate verrà generato il codice necessario a supportare la persistenza della classe su file-system, su SQLite o mediante Shared-Preference.

Esiste anche una libreria che consente di utilizzare Kripton con Retrofit, una libreria open source che si occupa di generare client REST e che vedremo in un una sezione successiva. Il suo nome è kripton-retrofit-converter.

 

Kripton: setup

Per poter utilizzare Kripton in un progetto di Android Studio è sufficiente includere nelle dipendenze del progetto i relativi artifact.

 

35-

 

 

Kripton: persistenza su file system

 

Kripton consente di rendere persistenti le istanze di una particolare classe con l’annotazione @BindType. Prendiamo ad esempio la classe User così definita:

 

36-

L’annotazione @BindType viene utilizzata dal processore di annotazione di Kripton per generare il codice necessario a convertire gli oggetti di tipo Java nei formati dati supportati da Kripton e viceversa. I formati supportati da Kriptono sono: JSON, XML, YAML, CBOR, (Java) properties e SMILE. Il codice per convertire un oggetto User in formato JSON è:

 

37-

Il risultato del codice è che nel file test.json verrà inserita la rappresentazione nel formato JSON dell’istanza dell’oggetto user.

 

38-

Kripton gestisce la conversione di tutti gli attributi di tipo primitivo, stringhe, array, collezioni di oggetti e singoli oggetti annotati con annotazione @BindType. E’ inoltre possibile raffinare la conversione di un particolare file mediante l’annotazione @Bind.

 

Kripton: conversione di altri formati dati

 

Nella sezione precedente abbiamo visto come sia possibile convertire un oggetto Java in JSON e viceversa con Kripton. Come è possibile osservare dal codice associato, la conversione di oggetti Java da e verso i vari formati dati avviene mediante la creazione di un BinderContext istanziabile dalla classe KriptonBinder.

Esiste un binder per ogni tipo di formato dati supportato da Kripton. Di base, Kripton offre i contesti di conversione per i formati JSON ed XML.

Per gli altri formati dati supportati da Kripton è necessario includere il relativo artifact e utilizzare il relativo BinderContext. Per convertire un oggetto Java in formato YAML è necessario scrivere:

 

39-

Per tutti gli altri formati, il codice per la conversione è praticamente identico, quello che cambia è il tipo di contesto di binding.

 

40-

Kripton consente di gestire la conversione in tutti questi formati grazie al fatto che si basa su un’altra libreria Java open source multi formato: FasterXML-jackson.

 

 

Kripton ORM: persistenza su SQLite

 

Un altro modo di rendere persistenti dei dati su piattaforma Android consiste nell’utilizzare SQLite. Android fin dalle sue prime versioni integra una versione del database SQLite. Come supporto a questo database, l’Android SDK offre, oltre alle classi che si interfacciano direttamente al suo engine, anche una classe SQLiteOpenHelper. Chi ha avuto modo di lavorare con questa classe sa che si tratta di un’operazione che richiede la scrittura di codice ripetitivo che porta via tempo e diventa facilmente fonte di bug.

In questo ambito ci viene incontro con il modulo ORM di Kripton che adotta il pattern DAO secondo il quale l’accesso ad una base dati avviene definendo:

 

# Data Access Object Interface: interfaccia dall’oggetto che contiene la logica di accesso alla base dati.

# Data Access Object: implementazione dell’interfaccia precedente.

# Model Object: il Plain Old Java Object (POJO) che rappresenta una tabella. Ogni suo field è associato ad una colonna della tabella associata alla classe.

 

L’insieme dei DAO atti a gestire una base dati viene definita all’interno della dichiarazione di data source che rappresenta la stessa base dati.

Kripton è in grado, grazie alle sue annotazioni e al suo processore di annotazioni, di generare l’implementazione del Data Access Object, comprensiva di javadoc e la classe che rappresenta la base dati o datasource. Ci sono due vincoli da rispettare:

 

# Ogni classe è associata ad un tabella ed ogni tabella a un classe.

# Ogni classe resa persistente deve avere un attributo di tipo long da utilizzare come primary key della tabella. In SQLite normalmente il nome della PK è id.

# Ogni DAO può produrre o liste di oggetti della classe ad esso associato o singoli oggetti sempre dello stesso tipo o semplici scalari.

 

Anche in questo caso Kripton supporta buona parte dei tipi inseriti nel JDK, tutti i tipi annotati con @BindTable o @BindType, e tutte le collezioni di oggetti sotto forma di array, liste o set.

Prendiamo ora come esempio la classe Person vista nel paragrafo sulla persistenza nel file system.

 

41-

Per inserirla in un modello di base dati SQLite è necessario dichiarare la relativa DAO interface ed il data source. Un esempio di interfaccia DAO:

 

42-

 

Kripton usa delle particolari annotazioni per definire le operazioni di SQL:

 

# @BindSqlSelect per le operazioni SQL di select.

# @BindSqlInsert per le operazioni SQL di insert.

# @BindSqlUpdate per le operazioni SQL di update.

# @BindSqlDelete per le operazioni SQL di delete.

 

La definizione del data source avviene grazie ad un’interfaccia annotata con @BindDataSource. Un esempio di definizione di data source:

 

43-

 

A fronte delle classi ed interfacce esposte, Kripton genererà, mediante il suo processore di annotazione in fase di compilazione, l’implementazione sia del data source che del DAO. Il nome del DAO sarà in questo caso UserDaoImpl, mentre per il datasource sarà BindSampleDataSource.

Generate queste classi, sarà possibile usare il codice d’esempio riportato di seguito che apre una connessione alla base dati ed effettua un insert:

 

44-

 

 

Kripton ORM: un esempio pratico

 

Analizziamo ora un esempio di modello dati utile per esplorare le funzionalità di Kripton come ORM:

 

45-

Ogni tabella è rappresentata da una classe Java e per ogni classe del modello è necessario definire l’interfaccia del DAO.

 

46-

 

Dal modello Java riportato possiamo ricavare le seguenti informazioni:

 

# qualunque attributo di classe, se non specificato diversamente, viene convertito in colonna.

# Le foreign key sono definite mediante campi di tipo long o Long e con l’attributo foreignKey dell’annotazione BindColumn.

# Di default il nome delle tabelle viene ottenuto convertendo i nomi delle classi (da upper camel a lower underscore). Un esempio: AlbumMusicale verrebbe resa persistente mediante la tabella album_musicale.

# Gli attributi complessi come le collection (list, set o mappe) sono gestiti senza problemi grazie al fatto che Kripton, per salvarli su SQLite, li converte nella loro rappresentazione JSON.

Vediamo ora la definizione delle interfacce DAO:

 

47-

 

Dalla definizione delle interfacce DAO possiamo ricavare le seguenti considerazioni:

 

# come i bean usati per definire il modello, anche le interfacce DAO possono usare l’ereditarietà per portar a fattor comune i metodi comuni a tutti i DAO.

# Le query possono essere definite parzialmente o per intero. In questo ultimo caso si scrive codice SQL con l’accorgimento di utilizzare i nomi di classi ed attributi e non quelli delle tabelle e delle colonne (JQL, Java Query Language).

# In fase di compilazione il codice JQL viene controllato, quindi in caso di errori di sintassi, la segnalazione avviene in fase di compilazione.

 

Oltre a quanto definito dal diagramma aggiungiamo altre due caratteristiche del modello: ogni tabella avrà il prefisso musica, vediamo poi come personalizzare i nomi delle tabelle mediante l’annotazione @BindTable e come definire le foreign key tra le varie tabelle.

 

Per quanto riguarda la definizione del data source è importate notare che, oltre all’elenco dei DAO associati alle tabelle, mediante alcuni attributi è possibile definire:

 

# name per il nome del database SQLite.

# version per il numero di versione dell’applicativo.

# asyncTask per abilitare o meno la generazione di un async-task con cui agevolare il lavoro con la base.

# rx per definire se generare o meno il codice necessario all codice di integrazione tra Kripton e RxJava. Chi non conosce la programmazione reactive e vuole apprendere le nozioni base di tale argomento, può consultare il progetto RxJava e la documentazione annessa.

 

 

Kripton ORM: classe del progetto

 

All’avvio dell’applicazione viene definita la base dati (solo la prima volta), cancellati i dati già esistenti ed infine caricati alcuni dati d’esempio, giusto per visualizzare qualcosa nell’activity principale del progetto. Vediamo la classe che rappresenta l’application del nostro progetto Android d’esempio.

 

48-

 

Con l’esecuzione del codice, sul log cat vengono generate le seguenti righe di log, utili a capire a runtime cosa succede:

 

49-

Come si vede dal log tutte le operazioni SQL, comprese quelle di creazione del database sono tracciate. Vediamo ora una parte del codice dell’activity al fine di capire come interagire con la base dati nell’activity o nei fragment.

 

50-

Nell’activity viene caricato e visualizzato l’elenco delle canzoni dalla base dati.

 

51-

Come si evince dal codice, per evitare di lavorare con il database sul thread UI (o main) si è utilizzato un async task generato da Kripton. Questo async task, a differenza di quelli “standard”, può essere riutilizzato diverse volte e consente di lavorare con il datasource generato in modo agevole (si veda il metodo onExecute). Ovviamente si poteva utilizzare event bus come sistema alternativo per fare la stessa cosa.

 

 

Kripton: SharedPreferences

 

La gestione delle shared preference in Android consente nativamente di gestire agevolmente le preferenze utente all’interno di un’applicazione. Kripton viene a supporto di questo meccanismo di persistenza dell’informazione fornendo una maggiore robustezza del codice necessario a recuperare e modificare tali preferenze per via programmatica. Prendiamo le shared preference del nostro progetto d’esempio.

 

52-

Il codice xml che definisce tale interfaccia è il seguente:

 

53-

 

54-

 

55-

 

Data una classe ExampleSharedPreference, Kripton genererà il codice necessario in BindExampleSharedPreference, ovvero una classe che ha il nome della classe originaria con il prefisso Bind. Anche in questo caso Kripton supporta diversi tipi di dati: stringhe, interi, collezioni, byte ed oggetti complessi. Tutto quello che non direttamente salvabile viene convertito nel sua rappresentazione JSON e poi salvato come stringa.

Ovviamente questo tipo di attributi non può essere utilizzato con i componenti standard per la gestione delle preference, ma può risultare utile nel caso in cui si necessiti o si voglia definire dei propri componenti per esse. Per poter utilizzare l’annotazione @BindSharedPreferences è necessario che il nome della classe termini con SharedPreferences.

Kripton, come abbiamo detto, è un progetto open source ospitato su GitHub.com. Esso è disponibile al seguente link. Nel progetto d’esempio associato a questa sezione è presente del codice che illustra l’uso di Kripton per persistenza mediante SQLite e le SharedPreferences.

 

 

La libreria Retrofit

 

Su piattaforma Android, parlare di servizi Web equivale a parlare di servizi REST. Non è obiettivo di questa guida parlare dei principi base dei servizi REST e delle differenze con i servizi SOAP. Per maggiori informazioni sui concetti base dei REST service si rimanda ad altri altri articoli come questo.

L’SDK di Android contiene ovviamente alcune classi atte a supportare la comunicazione mediante HTTP, alla base dei servizi REST. Esistono ovviamente diverse librerie open source scritte in Java che offrono supporto a tal fine.

La libreria che andremo a vedere in questa guida è una tra le più famose e si chiama Retrofit. Essa consente di accedere ai servizi REST semplicemente decorando con opportune annotazioni i metodi delle interfacce che rappresentano i servizi da utilizzare. Ecco un client REST definito con una delle annotazioni di Retrofit:

 

56-

In questo codice d’esempio abbiamo definito un metodo GET per recuperare una lista di oggetti User.

 

Retrofit: setup

Per poter utilizzare Retrofit nel suo funzionamento base è sufficiente includere la libreria tra le dipendenze del progetto.

 

57-

Retrofit da sola però non è in grado di gestire la deserializzazione del body della risposta HTTP, che normalmente contiene l’oggetto o gli oggetti che si richiedono mediante il servizio Web. Retrofit si integra con molte librerie di conversione tra JSON e Java. Tra queste possiamo annoverare anche Kripton che abbiamo visto nei paragrafi precedenti.

 

58-

 

Personalmente ho utilizzato l’integrazione con Kripton e quella con Gson.

 

Retrofit: esempio d’uso

 

Per meglio illustrare il funzionamento di Retrofit faremo riferimento ai servizi REST esposti dal sito jsonplaceholder.

Il servizio REST fornisce una risposta JSON simile a quella riportata nell’immagine seguente:

 

59-

Una possibile rappresentazione mediante classi Java dei dati recuperati dal servizio REST è data dalle seguenti classi (nel codice, per migliorare la leggibilità, sono stati omessi setter e i getter):

 

60-

La definizione del client REST per accedere servizio sopra descritto è data dalla seguente interfaccia

 

61-

L’istanza di Retrofit viene costruita con il pattern builder, specificando come informazione di minima l’URL di base del server che espone i servizi.

 

 

Retrofit: chiamate sincrone ed asincrone

 

Per invocare il servizio REST in modo sincrono è sufficiente scrivere:

 

62-

Tutto questo è possibile grazie alla classe Call che consente di eseguire il metodo sia in modo sincrono che asincrono. Nel caso di gestione asincrona, la gestione del risultato dell’invocazione del servizio REST viene effettuata mediante callback.

 

Retrofit: i methodi HTTP

 

Il servizio REST nell’esempio presentato nel paragrafo precedente risponde al verbo HTTP GET. Anche gli altri verbi HTTP possono essere utilizzati mediante annotazione. Per la definizione dei metodi HTTP sono a disposizione le  annotazioni: @GET, @POST, @PUT, @DELETE, @HEAD,@GET e @PATCH.

Ogni annotazione consente di aggiungere un pezzo di path in più rispetto al base url definito quando si inizializzava Retrofit. Per specificare un parametro dellaquery string è invece disponibile l’annotazione @Query

 

63-

All’interno dell’annotazione @GET, nella definizione del path relativo del servizio, è stato utilizzato un placeholder userId coincidente con il valore specificato nell’annotazione @Path.

 

Retrofit: passaggio di parametri

 

Retrofit consente di emulare l’invio di form con i relativi field:

 

64-

 

Retrofit: manipolazione di header

 

Un’altra funzionalità di Retrofit è la possibilità di aggiungere o modificare anche gli header. Ci sono due modi per definire un header: o mediante apposita annotazione, o mediante un parametro annotato con l’annotazione @Header:

 

65-

 

Retrofit: URL dinamici

 

Retrofit consente di definire l’url del servizio mediante apposito parametro decorato con l’annotazione @Url:

 

66-

 

Retrofit: download di file

 

Una delle operazioni che molto probabilmente avete già incontrato è quella relativa al download di file. Con Retrofit questo può essere fatto in modo decisamente semplice:

 

67-

Nel caso di file di una certa dimensione è meglio evitare di far passare il contenuto prima sulla RAM e poi sul disco. Questo si può ovviare con l’annotazione @Streaming.

 

68-

Retrofit è una libreria open source scritta in Java e disponibile su github

Il progetto proposto come esempio si appoggia ai servizi REST di JSONPlaceholder ed illustra l’integrazione tra Retrofit e Kripton.

 

69-

2 Risposte a “App Java per Android: librerie per ogni esigenza”

  1. EloisaBryant scrive:

    Great article! This is the kind of information that needs to be shared on the Internet. Too bad for search engines not to place this publication above!
    —————
    Ottimo articolo! Questo è il tipo di informazioni che devono essere condivise su Internet. Peccato per i motori di ricerca di non posizionare questa pubblicazione in alto!

  2. BraidenNiyah scrive:

    I visit websites every day to read posts, but this site has a very functional writing.
    —————
    Faccio una visita ogni giorno a siti web per leggere post,tuttavia questo sito presenta una scrittura molto funzionale.

Lascia una risposta