Sviluppo di una applicazione su piattafotma Android con il supporto

Università degli Studi dell’Aquila
Facoltà di Ingegneria
Tesi di Laurea in Ingegneria Informatica e Automatica
Sviluppo di una applicazione su
piattafotma Android con il
supporto di un DBMS orientato
ad oggetti
Relatore:
Laureando:
Prof. Serafino Cicerone
Marco Campoli
Anno Accademico 2010-2011
Indice
Introduzione
6
1 Dispositivi mobili ed Android
10
1.1
Perchè programmare per dispositivi mobili . . . . . . . . . . . . . . . 11
1.2
Sistemi per dispositivi mobili ed OHA
1.3
Android: Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . 14
1.4
1.5
. . . . . . . . . . . . . . . . . 12
1.3.1
Storia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
1.3.2
Versione della piattaforma . . . . . . . . . . . . . . . . . . . . 18
1.3.3
Java e Dalvik Virtual Machine (DVM) . . . . . . . . . . . . . 20
1.3.4
Architettura di Android . . . . . . . . . . . . . . . . . . . . . 22
1.3.5
ADT. Android Development Tool . . . . . . . . . . . . . . . . 26
1.3.6
Tipologie di applicazioni . . . . . . . . . . . . . . . . . . . . . 27
1.3.7
Componenti . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
1.3.8
Stato di un processo . . . . . . . . . . . . . . . . . . . . . . . 29
File androidmanifest.xml e risorse . . . . . . . . . . . . . . . . . . . . 31
1.4.1
Le risorse, struttura di un progetto e classe R . . . . . . . . . 33
1.4.2
Tipi semplici di risorse . . . . . . . . . . . . . . . . . . . . . . 34
1.4.3
Risorse associate ai file . . . . . . . . . . . . . . . . . . . . . . 35
1.4.4
La classe R . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Activity, Intent e comunicazione tra Activity . . . . . . . . . . . . . . 36
1.5.1
Activity . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
1.5.2
View e Layout . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
2
Indice
1.6
1.7
1.5.3
Intent e comunicazione tra Activity . . . . . . . . . . . . . . . 44
1.5.4
Gestione degli eventi . . . . . . . . . . . . . . . . . . . . . . . 45
Dialog, Toast, Widget e Notification . . . . . . . . . . . . . . . . . . . 47
1.6.1
Dialog . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47
1.6.2
Toast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
1.6.3
Notification Service . . . . . . . . . . . . . . . . . . . . . . . . 48
1.6.4
Home-screen Widget . . . . . . . . . . . . . . . . . . . . . . . 49
Gestione dei dati . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
2 Database ad oggetti e relazionali
2.1
54
Database Relazionali e RelationalDBMS . . . . . . . . . . . . . . . . 55
2.1.1
SQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
2.1.2
SQLlite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
2.2
ODBMS ed RDBMS . . . . . . . . . . . . . . . . . . . . . . . . . . . 60
2.3
DBMS ad oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61
2.4
Db4o . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
2.4.1
Licenza . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 66
2.4.2
Object Container ed Operazioni basilari . . . . . . . . . . . . 67
2.4.3
Query . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
2.4.4
Transizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
2.4.5
Proprietà ACID . . . . . . . . . . . . . . . . . . . . . . . . . . 72
2.4.6
Relazioni inverse . . . . . . . . . . . . . . . . . . . . . . . . . 73
2.4.7
Tipologie di relazioni ed Ereditarietà . . . . . . . . . . . . . . 73
2.4.8
Reference cache . . . . . . . . . . . . . . . . . . . . . . . . . . 75
2.4.9
Equivalence ed Equality . . . . . . . . . . . . . . . . . . . . . 75
2.4.10 Concetto di identità e trasparent persistence . . . . . . . . . . 77
2.4.11 Concetto di attivazione . . . . . . . . . . . . . . . . . . . . . . 78
2.4.12 Configurazione . . . . . . . . . . . . . . . . . . . . . . . . . . 80
2.4.13 Client-server mode . . . . . . . . . . . . . . . . . . . . . . . . 83
2.4.14 DRS. Data Replication System . . . . . . . . . . . . . . . . . 86
2.4.15 Db4o ed SQL (SQLite) . . . . . . . . . . . . . . . . . . . . . . 88
2.4.16 Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
2.5
Perst . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
3
Indice
2.5.1
Salvataggio degli oggetti . . . . . . . . . . . . . . . . . . . . . 91
2.5.2
Relazioni tra oggetti . . . . . . . . . . . . . . . . . . . . . . . 92
2.5.3
Attivare gli oggetti . . . . . . . . . . . . . . . . . . . . . . . . 93
2.5.4
Cercare Oggetti . . . . . . . . . . . . . . . . . . . . . . . . . . 93
2.5.5
Transizioni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
2.5.6
Relational Database Wrappers . . . . . . . . . . . . . . . . . . 94
2.5.7
Repliation System
2.5.8
Perst vs Db4o . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
. . . . . . . . . . . . . . . . . . . . . . . . 95
3 Realizzare un’applicazione Android-Db4o
97
3.1
Introduzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
3.2
Il problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
3.3
Casi d’uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
3.3.1
3.4
3.5
Casi d’uso Capionario . . . . . . . . . . . . . . . . . . . . . . 99
Architettura logica . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
3.4.1
MVC ed organizzazione dei package . . . . . . . . . . . . . . . 102
3.4.2
Pattern Singleton . . . . . . . . . . . . . . . . . . . . . . . . . 104
Modello di dominio . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
4 Analisi dei casi d’uso e SSD
108
4.1
System Sequence Diagram . . . . . . . . . . . . . . . . . . . . . . . . 108
4.2
Il nostro caso d’uso: Aggiungere un Cliente . . . . . . . . . . . . . . . 109
4.3
4.2.1
Aggiungere un Indirizzo . . . . . . . . . . . . . . . . . . . . . 110
4.2.2
Aggiungere Recapiti . . . . . . . . . . . . . . . . . . . . . . . 114
4.2.3
Aggiungere un Cliente . . . . . . . . . . . . . . . . . . . . . . 119
Modificare ed eliminare un Cliente
. . . . . . . . . . . . . . . . . . . 123
4.3.1
Modificare un Cliente . . . . . . . . . . . . . . . . . . . . . . . 123
4.3.2
Eliminare un Cliente . . . . . . . . . . . . . . . . . . . . . . . 125
5 Conclusioni
127
5.1
Il problema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
5.2
La soluzione . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
5.3
considezioni personali . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.4
sviluppi futuri . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
4
Indice
Bibliografia
132
A Programazione ad oggetti e Java
134
A.1 Programmazione ad oggetti . . . . . . . . . . . . . . . . . . . . . . . 134
A.2 Java: storia e caratteristiche . . . . . . . . . . . . . . . . . . . . . . . 135
B ORM ed Hibernate
138
5
Introduzione
Con la nascita dei moderni dispositivi mobili o smartphone è possibile la creazione
di applicazioni che si occupano di qualsiasi attività l’utente voglia eseguire. Con
l’inserimento di sistemi di localizzazione geografica GPS, fotocamere, riproduttori
mp3 ed altri strumenti, le capacità di questi device sono assimilabili a quelle di un
personal computer di circa otto anni fa, dai costi, in alcuni casi molto inferiori.
Le precedenti affermazioni fanno capire che il concetto di personal computer si
sta spostando verso dispositivi sempre più piccoli, che possono accompagnare i suoi
utilizzatori in ogni momento della giornata. Recenti statistiche affermano che il
numero di smartphone venduti nel mondo supera nettamente quello dei PC.
Caratteristica che accomuna ogni applicazione sviluppata per smartphone o calcolatore elettronico, è sicuramente la gestione di una grande quantità di dati. Ad
esempio, potrebbe essere necessario salvare elementi relativi un cliente o un prodotto, per poterli recuperare in un secondo momento. Questa situazione è associata
all’informatica fin dai suoi albori. Negli anni sono state realizzate soluzioni sempre
più performanti, concluse con la creazione delle basi dati e DBMS (Data Base Management System), cioè sistemi di gestione dei database.
Esistono varie tipologie di smartphone ed ognuno ha un proprio sistema integrato. La maggior parte di questi sono tecnologie proprietarie, cioè di dominio privato, facenti capo in generale ad una singola azienda. Tipici esempi sono Symbian
prodotto da Nokia o Ios prodotto da Apple.
In un mondo come l’informatica in cui si fanno spazio le teconologie open-source,
6
Capitolo 0
mancava certamente una piattaforma dedica a questi dispositivi. Con la l’avvento
di Android (prodotto da Google) sono stati rilasciati gli strumenti per la creazione
delle applicazioni e il sistema stesso, per cui molti programmatori si sono accostati
a questo tipo di progettazione.
Le ricerche effettuate sui vari devices fanno notare che la piattaforma Google è
quella che nell’immediato futuro e molto probabilmente nei prossimi anni sarà predominante nel mercato. Ad avvalere questa ipotesi è stato recentemente registrato
un numero di tre miliardi di applicazioni scaricate.
Spesso un’applicazione deve far riferimento ad una grande quantità di dati. Per
rendere persistenti questi valori si fa uso delle basi di dati. Nel 90% dei casi (che è
appunto la fetta di mercato occupata) parlando di un database ci si riferisce ad un
modello relazionale, cioè ad una struttura creata da tabelle, chiavi e relazioni.
Per eliminare qualche problema tra il linguaggio utilizzato per la realizzazione
di applicazioni in Android ed i sistemi relazionali, è possibile utilizzare una nuova
tipologia di DBMS che va sotto il nome di OODBMS (Object Oriented DBMS).
Esistono molti sistemi di gestione delle basi dati ad oggetti, peró si preferisce
concentrarsi su elementi open-source adatti all’integrazione in Android. Il risultato
della ricerca porta a due soli elementi che vanno sotto il nome di Db4o e Perst.
La tesi è corredata da un’applicazione di riferimento dedicata alla gestione di un
campionario di merci. Nella realtà esistono già molti apps che si occupano di questo
problema, però non tutte sono scaricabili gratuitamente o fanno uso di un DBMS
ad orientato agli oggetti.
Programmi di questo genere molte volte sono specifici per la gestione dei soli
prodotti; come si vedrà, BAgent oltre alle merci è in grado di gestire clienti, ordini, appuntamenti, tipologie per i prodotti e parametri opzionali aggiunti dinamicamente.
L’applicazione è stata realizzata mediante il Pattern MVC, il quale definisce chi e
come deve eseguire deteriminati compiti e il Pattern Singleton, usato per la gestione
degli elementi appartenenti allo stesso.
Come ampliamente consigliato dalla comunità di sviluppatori Android, BAgent
è stato realizzato mediante l’uso dell’IDE Eclipse per il quale è stato fornito l’ADT
7
Capitolo 0
(Android Development Tool), plugin messo a disposizione da Google, ed OME (Object Manager Enterprise), per la visione degli elementi contenuti nel database direttamente all’interno dell’IDE.
Per realizzare l’applicazione si è fatto uso di molti grafici. Per questi è stata
utilizzata la versione di prova di Poseidon for UML e strumenti di ingegneria inversa.
La struttura in capitoli del presente documento è racchiuso nella successiva
sezione:
Capitolo 1. In questo capitolo vengono forniti i motivi per i quali è consigliabile
sviluppare per dispositivi mobili e di conseguenza, vengono introdotti i sistemi di
maggior successo in questo ambito. In un secondo momento si introduce Android,
il sistema, gli elementi fondamentali e i motivi per cui l’applicazione BAgent è stata
realizzata con esso. L’ultimo paragrafo è dedicato ai vari metodi di gestione dei dati
per questa piattaforma, con il quale si introduce il capitolo successivo.
Capitolo 2. Dedicato allo studio dei DBMS ed in particolare degli ODBMS. Inizialmente vengono descritti gli RDBMS, SQLite e sistemi di mapping (ORM ). La
seconda parte è invece concentrata sullo studio dei database orientati ad oggetti,
in particolare su Db4o, Perst e sui confronti tra i vari sistemi di gestione dei dati.
Questo capitolo descrive anche i motivi per cui si è utilizzato Db4o per la realizzazione dell’applicazione.
Capitolo 3. Capitolo dedicato alla descrizione dell’applicazione di riferimento.
In esso sono racchiuse i concetti fondamentali dell’applicazione e la sua struttura.
BAgent è stato descritto sommariamente attraverso un modello realizzato con strumenti di ingegneria inversa di Poseidon for UML. Nella seconda parte vengono descritti i casi d’uso, cioè tutte le funzioni che l’app offre al suo utilizzatore, in questo
caso chiamato “Attore”. I casi d’uso sono divisi in quattro macro categorie nelle
quali l’attore che interagisce con l’applicazione è sempre lo stesso, cioè è l’utente
che la utilizza. Alla fine del capitolo è stata dichiarata la struttura in package del
progetto e le implementazioni dei Patterns Singleton ed MVC.
Capitolo 4. In questo capitolo si realizzano gli SSD o diagrammi di sequenza,
8
Capitolo 0
i quali descrivono le interazioni tra gli elementi caratteristici di BAgent. Avendo
sviluppato l’applicazione secondo il pattern MVC, questi grafici evidenziano le relazioni che avvengono tra i vari elementi che lo compongono. A partire da questi
elementi viene descritto anche il codice relativo al caso d’uso Aggiungi Cliente, nel
quale sono riportati i metodi per l’accesso e il recupero dati relativi ad indirizzi,
numeri di telefono, fax ed email direttamente dalla rubrica interna al dispositivo.
Nella parte finale si descrive il codice per recuperare un oggetto Cliente all’interno
del database ed effettuare operazioni di modifica o eliminazione su di esso.
Capitolo 5. Capitolo dedicato alle conclusioni tratte dallo studio di Android e degli
ODBMS. In esso sono raccolte riflessioni al problema interposto nella realizzazione
della tesi e alle sue soluzioni. L’applicazione di riferimento BAgent potrebbe inoltre
essere ulteriormente approfondita realizzandola in modo diverso per tablet, integrando nuove funzioni e rendendola disponibile per un numero maggiore di versioni
della piattaforma.
9
Capitolo
1
Dispositivi mobili ed Android
In questi ultimi anni si sta svolgendo una rivoluzione legata all’uso dei dispositivi
mobili 1 . I moderni dispositivi includono fotocamere, media players, sistemi GPS
e touchscreen. Queste caratteristiche aumentano le possibilità di utilizzo da parte
dell’utente finale e quindi la varietà di applicazioni che un programmatore può creare.
Fin dai primi anni in cui è cresciuto il mercato dei dispositivi mobili, essi erano sempre caratterizzati dall’avere un sistema proprietario. Ciascuna di queste
piattaforme aveva proprie caratteristiche, storia e linguaggio. Con l’avvento delle
tecnologie esposte in precedenza si è introdotta una barriera per i programmatori che
si distaccano da questo tipo di filosofia, poichè prediligono le tecnologie open-source
2
.
1
Con la notazione “dispositivi mobili” (o smartphone) si intende un qualsiasi
strumento elettronico capace di ricevere chiamate, caratterizzato dall’essere mobile.
Come si vedrà questa è solo una delle tante caratteristiche disponibili.
2
In un epoca segnata dall’uso di teconologie open source è sempre mancata una
piattaforma dedica ai dispositivi mobili. Come si vedrà, Android è caratterizzato
dal fatto di essere open source, quindi consultabile e migliorabile da chiunque voglia
farlo. La licenza scelta è la Open Source Apache Licence 2.0.
10
Capitolo 1
1.1
Perchè programmare per dispositivi mobili
Le informazioni che prima erano accessibili solo attraverso un personal computer,
ora possono essere recuperate attraverso dispositivi
3
che hanno la caratteristica di
essere mobili e di dimensioni sempre più ridotte. In questo modo si può estendere
il concetto di personal computer per dispositivi sempre più piccoli e che ci possono
accompagnare in qualsiasi momento.
Molto importanti sono le seguenti note:
• Il mercato dei dispositivi mobili ha superato quello dei personal computer
4
[1].
• Il 2009 è stato l’anno in cui il numero di accessi ad internet fatto con un
dispositivo mobile ha superato quello dei personal computer 5 .
• Ci sono molti fattori cui tener conto quando si scrivono applicazioni software
per smartphone.
• I dispositivi sono limitati da un piccolo schermo, una ridotta memoria e processori a bassa potenza 6 .
3
Rispetto un destkop o un notebook computer, i dispositivi mobili hanno: processori lenti, RAM limitata, memoria limitata, batteria limitata, piccoli schermi a
bassa risoluzione ed altri problemi. Ogni sviluppatore dovrebbe tener conto di essi,
anche se per ogni nuova generazione di telefoni diminuisce il gap prestazionale. Si
stima che la potenza di calcolo di un qualsiasi smartphone sia paragonabile a quella
di un personal computer di circa otto anni fa. Queste righe risalgono ad un articolo
scritto nel 2010 e al giorno d’oggi si può affermare che questo gap è stato ridotto
ulteriormente con l’avvento ad esempio di dispositivi dual-core.
4
Il boom del mercato degli smartphone è avvenuto tra il 2004 e 2005 dove furono
raddoppiate le vendite. L’ultimo dato fornito afferma che sono 21 milioni gli smartphone venduti nel mondo nell’ultimo trimeste del 2011 (a differenza dei normali
telefoni che hanno raggionto i 20 milioni di prodotti venduti).
5
Con 24 miliardi di device dotati di connessione wireless e 12 miliardi di device
connessi alla rete mobile attesi per il 2020 (per un mercato stimato di 1,2 triliardi
di dollari) stiamo decisamente entrando in una nuova era.
6
Questa caratteristica è tipica dei dispositivi rilasciati negli scorsi anni. Anche
se è buona norma assumere che essi siano a risorse limitate, va anche assunto il
fatto che come avviene per i personal computer, anche per gli smartphone o tablet
le prestazioni aumentano con il rilascio di nuovi dispositivi.
11
Capitolo 1
• Si deve sempre ottimizzare il codice per renderlo veloce e responsabile.
• Le applicazioni sono pensate per essere eseguite in piccoli schermi. Si dovrebbero creare applicazioni in modo da essere intuitive all’utente finale e che siano
ben visibili su schermi a diversa grandezza.
• Quando si sviluppano applicazioni che fanno uso della rete internet è buona
norma assumere che la connessione potrebbe essere lenta e molto costosa.
Alla base di quanto detto, la nuova frontiera per i programmatori è data dallo
sviluppo di applicazioni per telefoni di nuova generazione. Inoltre, è buona norma
tener conto di quali costi ogni azione coinvolge; in qualche caso come ad esempio l’uso
di GPS o il trasferimento dati sono richiesti costi aggiuntivi a seconda dell’operatore e
della tariffa utilizzata; l’utente ha sempre la possibilità di disabilitare queste funzioni
e lo sviluppatore è tenuto a rispettare queste scelte.
1.2
Sistemi per dispositivi mobili ed OHA
Il mercato degli smartphone può essere declinato attraverso tre grandi società che
vanno sotto il nome di Google, Nokia ed Apple. Google è responsabile del progetto
Android [5] [6] [7], mentre le altre due sono responsabili di Symbian
7
ed iOS 8 . I
produttori dei tre sistemi sono in continua disputa legale e letteraria 9 .
7
Prodotto della Symbian Foundation. La sua nascita risale al giugno del 1998
con la creazione di Symbian Limited nata dalla cooperazione di diverse compagnie
telefoniche. L’ultima versione disponibile del sistema operativo è la 5.0. Il 4 febbraio
2010 è diventato un sistema operativo libero, mentre Il 5 Aprile 2011 Nokia annuncia
un cambiamento nei criteri necessari per contribuire al progetto trasformandolo in
un sistema dove solo le aziende possono collaborare al suo sviluppo.
8
Sistema operativo sviluppato da Apple per iPhone, iPod ed iPad. Il sistema
operativo è stato presentato il nove gennaio 2007 al Macworld Conference Expo e
la versione 1.0, ancora priva di nome, è entrata in commercio con il primo iPhone
il 29 giungno dello stesso anno. L’ultima versione è stata rilasciata nel 2011 ed è la
quinta.
9
L’ultima disputa ripresa dai media risale a qualche mese fa. In una conferenza
esponenti importanti della Nokia hanno affermato che Google ha creato Android
grazie ad Apple (in qualche modo avrebbe copiato il sistema iOS).
12
Capitolo 1
Come visibile nel seguente grafico a torta (fornito da Millennial Media nell’agosto
2011), si può affermare che Google ed Apple sono i maggiori produttori di dispositivi
mobili.
Figura 1.1: Grafico delle vendite dei vari produttori di dispositivi mobili.
Come si vede bene dall’immagine, Android è il sistema utilizzato sul 54% dei
moderni telefoni, a fronte del 28% dei dispositivi iOS. Molto distante, con solo il
13%, si colloca RIM (casa produttrice dei BlackBerry) [2] [3].
Sicuramente Google ed Apple si contengono una grossa fetta di mercato. Questo
può essere dovuto dalla natura open-source di android e dalle innumerevoli caratteristiche ed accessori rilasciati dalla Apple per i suoi prodotti
10
.
Per quanto affermato bisogna specificare che andorid non è la risposta Google
all’IPhone. L’IPhone è un sistema hardware e software proprietario facente capo ad
una singola società (Apple). Android è uno stack open-source prodotto e supportato
dalla OHA.
10
Android non è da meno ad iOS. Sono recenti le notizie della brevettazione da
parte di Apple di un sistema che consente di effettuare qualsiasi operazione durante
una chiamata. È riscontrabile che nei dispositivi Android questo avveniva già da
molto tempo, ma in futuro dovrà essere eliminata a causa del brevetto.
13
Capitolo 1
La Open Handset Alliance (OHA) è un insieme di circa cinquanta società, dove
oltre a Google troviamo sviluppatori software, costruttori di componenti (Intel e
Texas Instruments) e MOBILE CARRIERS
11
. Di questa società fanno parte anche
le più note case costruttrici di dispositivi tra le quali si citano Motorola, T-Mobile,
Samsung, Sony-Ericsson e Toshiba.
1.3
Android: Introduzione
Android è un vero e proprio stack di strumenti e librerie caratterizzato da un sistema
costruito sul kernel open-source Linux (nella versione 2.6), da librerie ed API scritte
in C. Con queste caratteristiche ogni applicazione può avere accesso all’hardware
mediante specifiche API.
Android dispone di una vasta comunità di sviluppatori, i quali rilasciano nuove
applicazioni
12
(apps) che estendono le funzionalità del telefono. Per motivi di si-
curezza informatica, le apps possono essere scaricate e utilizzate mediante un servizio
di distribuzione fidato, quindi un sito internet che fornisca sia l’applicazione stessa
che le relative certificazioni
13
.
A differenza di altri sistemi, il linguaggio utilizzato da Android è Java
14
. In
questo modo nello sviluppo di applicazioni si ereditano tutti i pregi introdotti dalla
Sun, le sue API, ed inoltre ne vengono aggiunte ulteriori atte allo sviluppo di questo
tipo di applicazioni.
11
I mobile carriers sono letteralmente gli operatori di telefonia mobili. Di questo
gruppo fa parte ad esempio la Vodafone.
12
Attualmente sono disponibili nel market oltre 500000 applicazioni, dove la maggior parte di esse è scaricabile senza costi aggiuntivi. A dimostrazione del successo
del market è recenetemente stato raggiunto il numero di 10miliardi di app scaricate.
Per festeggiare l’avvenimento Google ha fornito il download di tutte le applicazioni
a pagamento alla modica cifra di 0.50 centesimi di euro.
13
Il sito in questione prende il nome di “AndroidMarket”. Per venire incontro alle
necessità degli sviluppatori è stata prevista la possibilità di disattivare il controllo
dei certificati a partire dalla versione 2.2 della piattaforma.
14
Java è un linguaggio di programmazione orientato agli oggetti descritto dalle
specifiche rilasciate da Sun nel 1995.
14
Capitolo 1
La scelta di Java [16] ha però un risvolto in contrasto con quella che è la natura
open di Android. I dispositivi che intendono adottare la Virtual Machine associata
all’ambiente J2ME (JVM o KVM) devono pagare una royality. Per rispondere a
questa esigenza Android non esegue bytecode Java, per cui non ha bisogno di una
JVM. Google ha adottato una propria VM che prende il nome di Dalvik 15 , la quale
trasforma il codice scritto in Dalvik dex-code (Dalvik Executable).
Le applicazioni sono sviluppate all’interno di un framework, ossia di una struttura
dati specifica. La struttura del framework è molto chiara se si utilizza l’ambiente di
sviluppo (SDK) con IDE Eclipse
16
.
Il Software Development Kit include tra le altre cose, gli strumenti di sviluppo,
librerie ed un emulatore. L’SDK è installabile su qualsiasi computer X86 e su uno
dei sistemi operativi di maggior uso, quali Linux, Windows o Mac.
Incluso nel Kit si trovano:
API: che garantiscono l’accesso allo stack Android da parte degli sviluppatori. Esse
sono le stesse con cui sono state create le applicazioni native.
Tools di sviluppo: strumenti rilasciati per compilare e fare il debug delle applicazioni.
Emulatore: l’emulatore è un dispositivo completamente interattivo per testare e
vedere le apps direttamente dal PC. Esso è eseguito nell’AVD (Android Virtual Device), il quale simula il dispositivo con le sue configurazioni hardware
all’interno di un personal computer.
Documentazione: informazioni dettagliate riguardanti la versione correntemente
installata nel PC.
15
Il nome Dialvik deriva da una località islandese. La scelta di utilizzare una
virtual machine diversa da quella di Java deriva dal fatto che si deve programmare
per un terminale mobile che risponde necessariamente ad eventi (touch schermo,
azioni da tastiera). Altri sostengono invece che questa scelta è dovuta solo alla
presenza di royality.
16
Eclipse è L’IDE ufficialmente supportata per lo sviluppo di applicazioni Android.
Per esso è stato fornito un plug-in che racchiude elementi per la creazione dei file
.xml e Java. Da sottolineare che Eclipse non è indispensabile per la creazione di
apps.
15
Capitolo 1
Esempi: collezione di esempi atti dimostrare le capacità di Android.
Senza alcun dubbio è nell’interesse dei creatori del sistema fornire strumenti
adeguati per la creazione di applicazioni. Infatti, il successo di una piattaforma può
essere diretta conseguenza del numero di estensioni disponibili per l’ambiente stesso.
Un motto molto ricorrente nei libri e negli articoli dedicati ad Android il seguente:
tutte le applicazioni sono uguali. Questo vuol dire che tutte le applicazioni, comprese
quelle native, sono scritte con le medesime API ed eseguite allo stesso tempo.
Ogni app. scritta in Android ha due caratteristiche predominanti:
• È caratterizzata da una parte dinamica scritta in Java ed una parte statica
XML che ne definisce il layout.
• È eseguita in un proprio processo, in una istanza della Dalvik.
1.3.1
Storia
Questo stack fu inizialmente sviluppato da Android Inc.
17
e successivamente ac-
quisito da Google. I cofondatori di Android Inc., Andy Rubin, Rich Miner, Nick
Sears (vicepresidente di T-Mobile) e Chris White hanno fatto parte della realizzazione della piattaforma in Google. Nel 2007 è stata rilasciata la prima versione
del Software Development Kit (SDK) che ha consentito agli sviluppatori di realizzare
le prime applicazioni sperimentali.
Il primo dispositivo Android, il T-Mobile G1, fu rilasciato nel lontano ottobre
2008 e alla fine del 2009 più di 20 smartphone furono lanciati per il mercato di circa
26 nazioni. Sempre alla fine del 2008 Google ha dato la possibilità agli sviluppatori
di alcuni paesi (tra cui non c’era l’Italia) di acquistare un telefono, il Dev Phone 1,
per sperimentare l’uso di applicazioni senza vincoli di operatori.
Subito dopo l’uscita della 1.0, si è iniziato a lavorare per la versione 1.1 (rilasciata nel dicembre 2008). Una delle limitazioni di essa era sicuramente quella che
obbligava i dispositivi ad usare una tastiera fisica. Con il rilascio della 1.5 (maggio
2009) fu introdotta la tastiera virtuale, in modo da eliminare il precedente vincolo
17
Android Inc. è stata fondata a Palo Alto California nell’ottobre 2003. Essa ha
fornito le basi del sistema Android.
16
Capitolo 1
18
. Questa versione è stata chiamata con un suo secondo nome “Cupcake” ed ha
identificativo API rappresentato dal valore 3
19
.
Il 16 settembre 2009 è stata rilasciata la versione 1.6 dell’SDK (chiamata anche
Donut) con diverse importanti novità, sia a livello utente, sia a livello di API (livello
assegnato a 4). In essa sono state implementate nuove funzioni e tecnologie come il
supporto alle reti CDMA
20
, diverse risoluzioni di schermo e un sistema di ricerca
globale (interno e su internet). I primi cellulari con Android 1.6 nativo sono stati
lanciati sul mercato globale poco dopo.
Nell’ottobre 2009, dopo poche settimane dal rilascio della versione 1.6, è venuto
il turno della distribuzione 2.0. Essa introduce la possibilità d’inviare dati tramite
Bluetooth, nuove API (identificate con il numero 7) e correzione di errori. Poco
dopo il rilascio della 2.0, il 12 gennaio 2010 è stato rilasciato l’Android SDK 2.1
(Eclair).
Il 20 maggio 2010 al Google I/O conference è stato presentato l’Android SDK
2.2 (chiamato anche Froyo). Sono stati inseriti importanti aggiornamenti in questa
versione: nuovo kernel linux 2.6.32, un nuovo compilatore JIT 21 , Tethering Wi-fi
per utilizzare il terminale come Hotspot Wireless
18
23
22
, oltre a nuove API (assegnate a
Atre importanti innovazioni di questa versione sono senzaltro la possibilità di
creare widget e folders.
19
Questo identificatore chiamato “API LEVEL” dichiara al sistema il livello di
compatibilità dell’applicazione. In questo caso come nelle successive distribuzioni è
stata garantita la retro compatibilità con le vecchie versioni.
20
Il Code Division Multiple Access (accesso multiplo a divisione di codice nota
anche con l’acronimo CDMA) è il protocollo di accesso multiplo a canale condiviso
più diffuso nelle retiwireless.
21
Un compilatore just-in-time o JIT permette un tipo di compilazione con la quale
è possibile aumentare le performance dei sistemi, traducendo il bytecode nel codice
macchina nativo in fase di run-time. L’obiettivo finale è di combinare i vantaggi
della compilazione del bytecode a quelli della compilazione nativa aumentando le
prestazioni.
22
Il tethering consiste nell’uso di un telefono cellulare come modem per offrire
accesso alla rete ad altri dispositivi che ne sono sprovvisti. La connessione tra i due
dispositivi può avvenire via Bluetooth, Wi-Fi o USB.
23
Con il termine Hotspot ci si riferisse ad un’area dove è possibile accedere su
internet in modalità senza fili attraverso l’uso di un Router collegato a un provider;
attualmente lo standard più diffuso in questo ambito è il Wi-Fi.
17
Capitolo 1
livello 8).
In questa relase è stata anche aggiunta la possibilità di installare le apps sulla
SD Card, mentre, il 7 dicembre 2010 è stata rilasciata la versione 2.3 cui è stata
assegnata la versione delle API a livello 9.
Nel gennaio del 2011 viene presentarto Honeycomb (3.0) dedicato ai soli tablet.
La versione definitiva dell’SDK 3.0 è stata invece ufficializzata solo il 23 febbraio
2011, mentre l’undici maggio 2011 è stata rilasciata la distribuzione SDK 3.1. Android 3.0 aggiorna il livello API al valore 13.
Nell’ottobre 2011 è stata presentata la versione 4.0 (Ice Cream Sandwich) destinata sia per smartphone che tablet, abbandonando la precedente situazione dove
smartphone e tablet utilizzavano sistemi operativi diversi. La relase aggiunge un
numero di features per gli utenti e i programmatori, alla quale si associa la versione
API con identificativo 14.
1.3.2
Versione della piattaforma
Gli sviluppatori Android forniscono mensilmente la stima dei dispositivi che hanno
avuto accesso all’Android Market (figura 1.2) e il numero di applicazioni disponibili
per una particolare versione (figura 1.3), in periodi uguali di una settimana [7].
Figura 1.2: Diagramma a torta degli accessi al market relativi ad ogni versione della
piattaforma (Febbraio 2012).
18
Capitolo 1
Figura 1.3: Diagramma delle applicazioni nel market disponibili per ogni versione
della piattaforma (Febbraio 2012).
La versione più diffusa risulta essere Froyo con il 27.8% di accessi. In costante
ascesa è invece Gingerbread nelle versioni 2.3.3 e successive (58.1%). Eclair è al
7.6%, mentre le versioni 1.x raggiungono in totale il 2% circa.
Bisogna sottolineare che Honeycomb fa fatica ad imporsi, segno del successo non
proprio esaltante dei tablet Android. Questa è una delle maggiori motivazioni che ha
indotto gli sviluppatori, pochi mesi dopo il rilascio della versione 3.0, ad introdurre
Ice Cream Sandwich, la quale è utilizzabile sia per smartphone che tablet (anche
questa statistica può considerarsi non pervenuta, poichè al momento è disponibile
solo per il dispositivo Nexus One. Notizie confortanti arrivano dai produttori di
smartphone, i quali dichiarano il rilascio di aggiornamenti alla versione 4.0 a partire
dalla prossima primavera).
Facendo riferimento alla precedente tabella ed hai dati rilasciati nei mesi scorsi
24
, si può affermare che gli sviluppatori possono contare su Froyo come piattaforma
24
Luglio 2010: le versioni 2.x occupano il 58,8% del diagramma, mentre le 1.x il
restante 41,2%.
Settembre 2010: La crescita dei dispositivi che si aggiornano all’ultima versione è
rassicurante. La versione più diffusa è Eclair (2.1) con oltre il 41% del mercato,
mentre al secondo posto troviamo l’ultimissima FroYo (2.2), che in pochi mesi recupera terreno a discapito di altre meno recenti. Si registra che il 29% degli utenti
utilizza ancora versioni 1.x.
Dicembre 2010: in questo mese finalmente avviene il sorpasso di Froyo (2.2) su Eclair
19
Capitolo 1
e fare uso senza troppi problemi delle API di sistema di livello 8.
Come si vede dal diagramma numero 1.2, uno dei problemi caratteristici in Android è la frammentazione delle sue distribuzioni. Anche se esistono versioni predominanti, ci sono moltissimi utenti che fanno ancora riferimento a quelle vecchie.
Questa situazione può essere data dal fatto che i produttori di smartphone non
hanno rilasciato una nuova versione della piattaforma per tutti i dispositivi (motivo dovuto ai costi che ogni produttore dovrebbe investire per l’aggiornamento del
sistema). Questo problema si ripresenta quando un utente vuole scaricare una determinata applicazione, a seconda della versione del proprio smartphone essa potrebbe
non essere disponibile.
Solo recentemente si è raggiunta una soluzione: gli sviluppatori possono inserire
più volte la stessa applicazione nel market, in riferimento alle diverse versioni del
sistema.
1.3.3
Java e Dalvik Virtual Machine (DVM)
Android sfrutta il linguaggio di programmazione Java nella versione 5 per la creazione
delle applicazioni. Sono implementate la quasi totalità delle API, ma sono state escluse le Abstract Window Toolkit (AWT
25
) e le Swing
26
. La scelta di utilizzare
(2.1), con percentuali rispettivamente del 43,4% e del 39,6% (l’80% del totale).
Febbraio 2011: Android 2.2 Froyo rappresenta il 57,6% del mercato, mentre Eclair
scende al 31.4%. Android 1.6 scende al 6,3% e Android 1.5 al 3,9%. Si nota anche la
presenza di Android 2.3 Gingerbread, attualmente presente solo sul Nexus S (circa
1%).
Ottobre 2011: Android 2.2 Froyo rimane la versione più diffusa con una percentuale
del 45.3%, mentre Gingerbread rappresenta il 38.2%. Al terzo posto troviamo Android 2.1 Eclair con una percentuale ancora alta, 11.7%; seguono infine Android 1.6
Donut con l’1.4%, Android 1.5 Cupcake con l’1.1% e rimane ancora poco diffuso,
Honeycomb, le cui versioni risultano sotto il punto percentuale.
Gennaio 2012: mese in cui viene riportata per la prima volta la versione 4.0 della
piattaforma.
25
La Abstract Window Toolkit (AWT) è la libreria Java contenente le classi e le
interfacce fondamentali per la creazione di elementi grafici. Essa è stata inserita
nelle API standard di Java per lo sviluppo di applicazioni GUI.
26
Swing è un framework per Java orientato allo sviluppo di interfacce grafiche.
Parte delle classi del framework Swing sono implementazioni di widget, come caselle
20
Capitolo 1
Java forse è dovuta al fatto che un’applicazione compilata può essere eseguita senza alcuna modifica in sistemi operativi diversi, a patto che per questi esista una
specifica JVM in grado di tradurre le istruzioni Bytecode in codice nativo.
Come già visto nell’introduzione, Android ha adottato una propria VM che
prende il nome di Dalvik. Si tratta di una VM ottimizzata per l’esecuzione di applicazioni in dispositivi a risorse limitate, la quale esegue codice contenuto all’interno
di file di estensione .dex, ottenuto a sua volta in fase di building, a partire da file
.class di bytecode Java. Ufficialmente questa scelta è stata dettata dalla necessità
di risparmiare memoria per la memorizzazione e l’esecuzione delle applicazioni.
Da un’applicazione Java descritta da codice con estensione .jar, si ha circa il 50%
in più di memoria della stessa applicazione con estensione .dex. Questa diminuzione
avviene in fase di trasformazione dal bytecode Java al bytecode per la DVM, durante
il quale i diversi file .dex sono in grado di condividere informazioni che altrimenti
verrebbero ripetute più volte.
Un altro aspetto molto importante della DVM riguarda il meccanismo di generazione del codice che viene detto register based, a differenza di quello della JVM
detto invece stack based. Attraverso questo meccanismo i progettisti della DVM si
aspettano, a parità di codice Java, di ridurre del 30% il numero di operazioni da
eseguire. Per capire come questo possa avvenire si propone un semplice esempio.
Supponiamo di voler valutare la seguente operazione:
c= a+b;
Se con L indichiamo una operazione di Load e con S indichiamo quella di Store, la
precedente operazione si può tradurre nel seguente modo:
push b; //LS
push a; //LS
add; //LLS
store c; //LS
Se volessimo ora eseguire la stessa operazione con un meccanismo register based
otterremo:
add a,b,c; //LLS
Questa ultima istruzione mostra il caricamento degli operandi a e b in zone diverse
di testo, pulsanti, pannelli e tabelle.
21
Capitolo 1
di un registro e la memorizzazione del risultato in c. In questo caso si ottiene un
minor tempo di esecuzione delle istruzioni, al prezzo di un maggior sforzo in fase di
compilazione o trasformazione.
1.3.4
Architettura di Android
Android è un’architettura che comprende tutto lo stack degli strumenti per la
creazione di applicazioni per dispositivi mobili, dove i layer inferiori forniscono strumenti a quelli superiori. In questa struttura si nota la presenza di un sistema
operativo, un insieme di librerie native, ed una implementazione della VM.
Lo stack è composto dagli elementi in figura 1.4, i quali sono trattati in dettaglio
nelle prossime righe.
1. Linux Kernel: il layer più basso del dispositivo nella versione 2.6 di linux. La
necessità è quella di disporre di un vero e proprio sistema operativo che fornisce
strumenti di basso livello, attraverso la definizione di diversi driver. In particolare, si possono notare la presenza di driver per la gestione delle periferiche
multimediali, del display, della connessione Wi-Fi e dell’alimentazione.
2. Librerie: layer superiore al kernel che include varie librerie, le quali fanno
riferimento a un insieme di progetti Open Source C/C++. Tra esse si citano:
• Surface Manager (SM): componente fondamentale che ha la responsabilità di gestire le View, ovvero ciò di cui l’interfaccia grafica è composta.
La SM ha il compito di coordinare le diverse finestre che le applicazioni
visualizzano sullo schermo.
• Open GL ES: libreria utilizzata per la grafica 3D, la quale permette
l’accesso alle funzionalità di un eventuale acceleratore grafico hardware.
Si tratta di una versione OpenGL specializzata per dispositivi mobili.
• Scalable Graphics Library (SGL): libreria C++ utilizzata per la
grafica 2D.
• Media Framework: la maggior parte delle applicazioni Android sono
caratterizzate da un elevato contenuto di componenti multimediali. Vi è
22
Capitolo 1
Figura 1.4: Stack Android.
23
Capitolo 1
la necessità di un componente in grado di gestire i diversi CODEC 27 per
i vari formati audio e video. Questo componente è basato sulla libreria
open source OpenCore ed è fornita da Packet Video (uno dei membri
fondatori dell’OHA).
• FreeType: motore di piccole dimensioni ed efficiente, utilizzato per i
Free Font Type
28
. Attraverso FreeType le aplicazioni sono in grado di
visualizzare immagini di alta qualità.
• SQLite: libreria che implementa un DBMS relazionale caratterizzato
dall’essere molto compatto.
• WebKit: si tratta di un browser engine
basato sulle tecnologie HTML
30
27
, CSS
31
29
(e non un semplice browser)
, JavaScript
32
e DOM
33
.
Un codec è un programma o un dispositivo che si occupa di codificare e/o
decodificare digitalmente un segnale analogico (tipicamente audio o video), affinchè
possa essere salvato su un supporto di memorizzazione o richiamato per la sua lettura
o riproduzione.
28
Insieme di caratteri tipografici caratterizzati e accomunati da un certo stile
grafico.
29
Componente software che interpreta delle informazioni in ingresso codificate
secondo uno specifico formato e le elabora creandone una rappresentazione grafica.
Nel caso di un browser vengono interpretati gli stili associati ai documenti scaticati
e li rappresenta sul monitor.
30
In informatica l’HTML (HyperText Markup Language o linguaggio di descrizione per ipertesti) è il linguaggio di markup (che definisce modalità di impaginamento formattazione e visualizzazione dati) solitamente usato per i documenti
ipertestuali disponibili nel World Wide Web. In tali documenti, un tratto di testo
può essere contrassegnato inserendo dei tag, che ne descrivono la funzione, il colore
o altre caratteristiche.
31
Il CSS (Cascading Style Sheets o Fogli di stile) è un linguaggio usato per definire
la formattazione di documenti HTML ed XML. I fogli di stile permettono di separare
i contenuti dalla formattazione e permettere quindi, una programmazione più chiara
e facile da utilizzare.
32
JavaScript è un un linguaggio object based comunemente usato per l’esecuzione
di controlli nei siti web lato client. Altra caratteristica fondamentale è che esso è
in grado di rispondere ad eventi; al contrario di quanto si possa pensare, Javascript
non è un parente di Java.
33
Il DOM (Document Object Model, letteralmente modello a oggetti del documento) è una forma di rappresentazione dei documenti strutturati come modello
24
Capitolo 1
• SSL: libreria usata per la gestione dei Secure Soket Layer atta ad osservare gli aspetti legati alla sicurezza nello scambio di comunicazioni.
• Libc: si tratta di un’implementazione della libreria standard C ottimizzata per dispositivi basati su linux embedded come Android.
3. Android run time: include librerie core e la DVM. Questo è il motore delle
applicazioni e forma le basi dell’application frameworks.
• Core Library: classi relative all’ambiente nella quale l’applicazione
viene eseguita; le core library sono rappresentate da pacchetti di formato
.dex.
4. Application Framework: tutte le librerie viste fino a questo momento vengono utilizzate da componenti di più alto livello che compongono l’Application
Frameworks. Questo livello fornisce le classi usate per creare le applicazioni.
Tutte le apps Android utilizzano lo stesso AF (da qui è possibile trovare un’ulteriore conferma del motto ricorrente: All Applications Are Equals). In questo
layer possiamo trovare:
• Activity Manager: responsabilità di questo componente è l’organizzazione delle varie schermate di un’applicazione a seconda dell’ordine di
visualizzazione delle stesse sullo schermo. Ogni schermata è rappresentata
da un’Activity.
• Package Manager: responsabilità del package manager è quella di
gestire il ciclo di vita delle applicazioni.
• Window Manager: componente che permette di gestire le finestre delle
diverse applicazioni sullo schermo del dispositivo.
• Telephony Manager: permette l’interazione con le funzionalità caratteristiche di un telefono, come la possibilità di iniziare una chiamata o di
verificare lo stato della stessa.
orientato agli oggetti. DOM è lo standard ufficiale del W3C per la rappresentazione
di documenti strutturati, in maniera da essere neutrali sia per la lingua che per la
piattaforma. Sfortunatamente negli scorsi anni questa struttura non è mai stata
presa come riferimento, rendendo molto difficile la vita dei programmatori web.
25
Capitolo 1
• Content Provider (CP): componente fondamentale nella realizzazione
delle applicazioni Android, poichè ha la responsabilità di gestire la condivisione di informazioni tra vari processi. Il funzionamento è simile a quello di un repository, in cui diverse applicazioni interagiscono, inserendo o
leggendo informazioni.
• Resource Manager: ha la responsabilità di gestire un insieme di file di
tipo diverso, tra cui immagini, file di configurazione o di ottimizzazione
delle risorse.
• View System: compito del View System è la gestione degli eventi
associati alle applicazioni.
• Location Manager: API che prendono il nome di Location Based Application (LBA). Queste permettono di gestire le attività con il sistema
di Geo Referenziazione (GPS).
• Notification Manager: il Notification Manager mette a disposizione un
insieme di strumenti per inviare particolari notifiche al dispositivo. Tra
gli avvisi è possibile citare la vibrazione o visualizzazione di un’icona.
1.3.5
ADT. Android Development Tool
L’SDK include molti tool ed utilities per aiutare i programmatori a creare, testare
ed effettuare debug. L’Android Development Tool contiente molti di questi elementi
che fanno uso dell’IDE Eclipse. Tra questi si possono citare:
SDK e Virtual Device Manager: usato per creare e gestire l’Android Virtual
Device, cioè un’istanza dell’emulatore in grado di simulare specifiche hardware ed API disponibili per diversi dispositivi. Questa soluzione permette agli
sviluppatori di testare il software per diversi schermi e risoluzioni.
Emulatore Android: utilizzato dagli sviluppatori per testare ed effettuare debug
delle applicazioni. Con l’emulatore è possibile simulare SMS, chiamate e tempi
di latenza.
Dialvik Debug Monitoring Service (DDMS): usato per monitorizzare e controllare la DVM quando di effettua il debug dell’applicazione.
26
Capitolo 1
1.3.6
Tipologie di applicazioni
Le applicazioni sviluppabili in ambiente Android possono essere raggruppate nelle
seguenti quattro categorie:
Foregound: un’applicazione è detta in foreground quando interagisce con l’utente
e viene sospesa quando non è visibile. Con elementi di questo tipo bisogna
considerare il ciclo di vita delle Activity (introdotto nei prossimi paragrafi).
Background: applicazione con limitata interazione con l’utente che si trova per
la maggior parte del tempo nascosta, in modo da ascoltare le azioni causate
dall’hardware, dal sistema o da altre applicazioni. Esse si trovano in foreground
solo nel caso in cui debbano essere configurate
34
dall’utente.
Intermittent: alcune applicazioni sono interattive ma fanno la maggior parte del
lavoro in background. Queste sono generalmente una unione delle Activity
con i Background Services. Estensioni della piattaforma che gestiscono chat
ed email sono tipici esempi.
App Widget: alcuni elementi sono rappresentati solo da una schermata nella home
del dispositivo mobile. Questi vanno appunto sotto il nome di widget.
Alcune Applicazioni possono essere molto complesse, quindi molto difficili da far
rientrare in una delle precedenti categorie (in alcuni casi è possibile racchiudere le
precedenti in un unico elemento).
1.3.7
Componenti
Le estensioni Android devono garantire un certo livello di interattività con gli utenti.
Questo deve avvenire in modo altamente intuitivo e senza alcuno spreco di risorse.
I progettisti hanno pensato di fornire alcuni componenti e un meccanismo di
comunicazione tra essi, in modo da permettere un’ottimale sfruttamento delle delle
risorse ed estensibilità della piattaforma.
34
È buna norma implementare la funzione di configurazione all’interno delle applicazioni di tipo Background. Questa procedura rende possibile il settaggio di
opzioni.
27
Capitolo 1
I componenti costruiscono i blocchi principali delle applicazioni e possono essere
racchiusi in:
Activity: ogni schermata è un estensione della classe Activity. Le Activity usano
le Views per avere un’interfaccia grafica con l’utente, le quali rispondono alle
azioni degli stessi.
Siccome un’applicazione può essere composta da più schermate, la piattaforma
organizza le attività secondo una struttura a stack, dove l’attività in cima è
quella attiva in un particolare momento. La visualizzazione di una nuova
schermata porterà questa in cima allo stack, ponendo in pausa la precedente.
Quando una Activity termina il proprio lavoro fa in modo di ritornare le eventuali informazioni raccolte. Il sistema rende tutte queste operazioni trasparenti
all’utente che usa il dispositivo, mentre lo sviluppatore deve gestire gli stati di
un’attività attraverso opportuni metodi di callback
35
.
Service: i componenti Services operano in background facendo l’upload delle risorse
e delle Activity visibili. Essi vengono spesso citati con la notazione: “invisible
workers of your application”.
Content provider: utilizzati per gestire le repository di informazioni. Android
include alcuni Content Provider nativi che si riferiscono ad informazioni ad
esempio circa i contatti telefonici, chiamate, eccetera.
Intent e intent filter: quando un’applicazione ha la necessità di eseguire una particolare operazione non fa altro che creare creare un Intent, richiedendo l’utilizzo di una qualunque risorsa o componente in grado di poterla esaudire. Un
Intent deve essere caratterizzato da informazioni relative all’acquisizione dei
35
Con questo termine si intende una funzione che viene richiamata da un’altra
funzione o dal sistema operativo; questi metodi sono in grado di gestire determinati
eventi.
28
Capitolo 1
dati e da un meccanismo per identificare il tipo. Questi dati sono rappresentati
mediante un oggetto di tipo String e da una URI
36
.
A completamento di quanto scritto, serve un meccanismo che permetta ad un
applicazione di dichiarare l’insieme degli Intent che gli stessi sono in grado di
gestire. Questo viene realizzato attraverso gli Intent filter.
Broadcast receivers: La gestione di eventi esterni può essere realizzata attraverso
la definizione di Broadcast receiver.
L’arrivo di un evento esterno implica l’attivazione di un Intent receiver ma non
necessariamente l’esecuzione di una Activity e nemmeno la notifica all’utente.
Notification: le notifiche forniscono un input all’utente che non perdere il focus
dell’Activity corrente. Questa tecnica fornisce segnali da parte di un Service
oppure un Broadcast receiver.
1.3.8
Stato di un processo
Come introdotto nei precedenti paragrafi, ogni applicazione fa riferimento ad un proprio processo in un istanza della Dalvik Virtual Machine. Se i processi in esecuzione
all’interno del dispositivo esauriscono la memoria, ne vengono eliminati alcuni al
fine di favorire l’esecuzione di quelli visibili.
Il sistema sceglie quale processo terminare in base a determinate priorità. A tale
proposito, le tipologie sono state classificate in:
• Active process.
• Visible process.
• Service process.
• Background process.
36
URI: Uniform Resource Identifier. Permette di specificare molti tipi di dato secondo la notazione “scheme://host:port/path”, dove si definisce authority l’insieme
dell’host e della porta. Insieme all’URI è molto importante il concetto di mime-type
dei dati associati, il quale è caratterizzato da una coppia chiave-valore.
29
Capitolo 1
• Empty process.
dove la figura 1.5 mostra l’albero delle priorità.
Figura 1.5: Albero delle priorità.
Active process: componenti di applicazioni che interagiscono con l’utente. Di
norma all’interno del dispositivo ci sono pochi processi di questo tipo. Essi
hanno priorità impostata al valore Critico, quindi verranno terminati solo nel
caso in cui non fossero disponibili le risorse per la loro esecuzione.
Visible process: processi visibili ma inattivi. Un processo si trova in questo stato
quando una Activity è parzialmente visibile all’utente ma allo stesso tempo
oscurata. La priorità assegnata a questi processi prende il nome di High.
Started Service Process: processi che non hanno interfaccia grafica ma con priorità paragonabile a quella delle Activity allo stato di RUNNING. Si provvede
alla loro eliminazione solo nel caso di reale necessità, ovvero per non precludere
l’esecuzione dei processi elencati in precedenza. Anche questi processi hanno
priorità impostata ad High.
30
Capitolo 1
Background Process: in genere esiste un gran numero di processi in background
che vengono terminati usando il pattern LSFK. La priorità è impostata a Low.
Empty Process: si tratta di processi legati a nessun componente predefinito della
piattaforma e quindi tra i primi candidati all’eliminazione. Si parla di empty
process perchè Android di solito mantiene in memoria applicazioni anche dopo
aver terminano il loro ciclo di vita.
1.4
File androidmanifest.xml e risorse
Ogni applicazione Android include il file AndoridManifest.xml salvato nella root
del progetto. Questo è utilizzato per definire la struttura dei meta-data dell’applicazione, i suoi componenti e le sue richieste sotto forma di file XML.
Il file include i nodi per componenti quali Activity, Services, Content Provider,
Broadcast Receiver (trattati nel seguito), un’icona da visualizzare nel menu, una
label per il nome dell’applicazione e temi. È inoltre necessario definire permessi
che determinano come l’applicazione può interagire con l’utente e come essa possa
interagire con altre applicazioni.
L’Android Manifest ha come elemento di root il tag manifest. In questo tag
vendono definiti gli attributi versioncode, utilizzato per definire la versione corrente
dell’applicazione, e l’attributo versionname, che specifica la versione visibile agli
utenti.
<m a n i f e s t xmlns : a n d r o i d :
// schemas . a n d r o i d . com/ apk / r e s / a n d r o i d
package= ”com . my domain . my app”
a n d r o i d : v e r s i o n C o d e= ”1”
a n d r o i d : versionName= ” 0 . 9 Beta”>
[
]
</m a n i f e s t >
Il tag manifest può contenere molti nodi a seconda delle richieste dell’aplicazione.
Il primo di essi è sicuramente uses-sdk che è in grado di definire la versione minima
31
Capitolo 1
37
, massima
38
ed il target SDK che può essere utilizzato.
<uses −sdk
a n d r o i d : minSdkVersion= ”4”
a n d r o i d : t a r g e t S d k V e r s i o n= ”5”>
</u se s −sdk>
Subito dopo il tag manifest è possibile trovare il nodo application utilizzato per
specificare i meta-data delle applicazioni.
<a p p l i c a t i o n
a n d r o i d : i c o n= ” @drawable / i c o n ”
a n d r o i d : theme= ” @ s t y l e /my theme”
a n d r o i d : name= ” MyApplication ”
a n d r o i d : debuggable= ” t r u e ”>
[
]
</ a p p l i c a t i o n >
Il precedente elemento può contenere a sua volta i tag Activity, Services, Content
Provider e Broadcast Receiver che specificano i componenti dell’applicazione.
All’interno del nodo application è possibile trovare uno o più nodi activity,
richiesti per il corretto funzionamento di ogni Activity visualizzata sullo schermo.
Usando l’attributo android:name si specifica il nome della classe Activity. Ogni
nodo di questo tipo può contenere intent-filter
39
, il quale specifica quale Intent può
eseguire l’Activity.
37
Il parametro segnala la versione minima delle API in cui l’applicazione non
genera errori. Nel caso in cui si omette l’attributo minSDKVersion, l’applicazione
fallirà l’esecuzione nel momento in cui si cerca di accedere ad API non disponibili
per quel dispositivo. La scelta multipla delle proprietà introdotte non è consentita,
poichè, si suppone che un dispositivo compatibile con una determinata versione sia
in grado di eseguire applicazioni sviluppate per le versioni precedenti.
38
Inserendo l’attributo maxVersion non sarà possibile installare l’applicazione in
dispositivi con livello API maggiore. È buona norma non inserire questo attributo
se non si riscontrano problemi con nuove versioni delle API.
39
Pensando ad un intent come la volontà di un particolare componente di eseguire
una determinata azione su un determinato insieme di dati, un intet-filter è invece la
dichiarazione da parte di un componente di essere in grado di soddisfare un intent.
32
Capitolo 1
Il tag service è utilizzato per segnalare la presenza di una classe Service utilizzata
nell’applicazione. Anche questo nodo può contenere tag figli di tipo intent-filter.
Come nei casi precedenti, i nodi provider e receiver vengono utilizzati per segnalare content provider e broadcast receiver rispettivamente. Altro nodo di notevole
importanza è uses-permission, il quale permette di dichiarare i permessi che l’applicazione dovrà ricevere dall’utente per consentire il corretto funzionamento della
stessa. Se un’applicazione richiede specifici permessi da parte dell’utente, essi saranno esposti prima dell’installazione della stessa. Nel caso in cui l’utente non accetta
l’uso di questi, l’applicazione non sarà installata.
Come si può intuire, una corretta gestione del file manifest potrebbe essere molto
difficile. Fortunatamente gli sviluppatori hanno hanno messo a disposizione un
esenzione per l’IDE Eclipse in grado di gestire in modo visuale quest’ultimo.
1.4.1
Le risorse, struttura di un progetto e classe R
Potrebbe essere molto produttivo estraniare le risorse di un progetto e renderle
disponibili attraverso delle costanti. Android supporta questo sistema per molti
elementi che vanno da un semplice valore come una stringa o un colore, per finire
con immagini (Drawable), animazioni e temi.
Nel momento in cui si rende necessario l’utilizzo delle risorse, l’applicazione sa
dove si trova il corretto valore da visualizzare, quindi questo meccanismo è del
tutto trasparente all’utente e come si vedrà può essere trasparente anche verso un
progettista.
Le risorse possono essere di due tipologie: quelle che vanno sotto la cartella
res e quelle nella cartella di nome assets. Le prime sono compilate in un formato
conveniente e sono accessibili attraverso costanti generate dalla classe R.
Nella cartella res sono disponibili di default le sotto-cartelle values, drawableldpi, drawable-mdpi, drawable-hdpi, utilizzate per la gestione di elementi drawable
per diverse tipologie di schermi. Di questa cartella fanno parte anche le risorse
layout e non compilate (raw ) per le quali viene comunque generato in identificativo
nella classe R.
La seconda cartella va sotto il nome di assets e contiene risorse che mantengono
il loro stato di origine, cioè non vengono compilate. Queste tipo di risorse possono
33
Capitolo 1
essere gestite solamente attraverso la classe AssetManager.
1.4.2
Tipi semplici di risorse
I tipi semplici includono string, colors, dimensions, integer-array e string-array.
Tutti questi tipi sono salvati come file XML ed inclusi della cartella res/values.
String: specificate dal tag string ed associate ad una chiave univoca nell’applicazione.
Integer e String Array: queste risorse contengono rispettivamente array di interi
o stringhe. Elementi di questo tipo possono essere creati mediante l’uso di tag
item, dichiarati a loro volta all’interno di string-array o integer-array.
Color: usando il tag color si specificano risorse di tipo colore. I colori vengono specificati usando il simbolo # seguito da uno o due numeri esadecimali per i rispettivi valori di rosso, verde e blù. È inoltre possibile l’inserimento di un’ulteriore
coppia esadecimale, la quale indica i parametri alpha di trasparenza.
Drawable: definite nella cartella res/drawable, alle quali rientrano le immagini e
Drawable composti come ad esempio LevelListDrawable e StateListDrawable.
Da segnalare che non tutte le specializzazioni della classe Drawable possono
essere definite in modo dichiarativo attraverso documenti XML.
Stili e temi: uno stile definisce un insieme di attributi che possono essere applicati
ad un particolare componente, View o UI. Per gli stili si ha un tag di tipo style
in cui definiamo un attributo di tipo name, contenuti da uno o più tag item.
Mentre uno stile si applica a un componente, un tema è un insieme di stili che
si impostano a livello di applicazione o di singola attività.
È possibile ereditare le proprietà di un tema già definito attraverso l’attributo
parent, contenente il nome del tema specifico. Come nel precedente caso si
può dichiarare un insieme di sotto nodi item, ognuno con lo specifico attributo
name.
34
Capitolo 1
Dimension: il tag dimen seguito da un identificatore di scala definisce risorse di
tipo dimension. Questi parametri possono essere utili per creare costanti di
altezza e larghezza espressi in px, mm, dp, sp.
Animations: Android supporta due tipi di animations, le Tweened animations,
usate per ruotare e muovere le View, oppure le frame-by-frame animations, le
quali visualizzano una sequenza di immagini Drawable.
Layouts: risorse utilizzate per disegnare le user interface o elementi customizzati
attraverso documenti XML. Le activity vengono messe in relazione con i layout
attraverso l’uso del metodo setContentView e sono salvati in un file separato
all’interno della cartella /res/layout.
1.4.3
Risorse associate ai file
Le risorse associate ai file non si traducono in oggetti particolari, ma possono essere utilizzate in base alle loro specifiche caratteristiche. In questo caso si parla
di determinati file binari o documenti XML che possono racchiudersi nei seguenti
punti.
Assets: si tratta di file che non corrispondono a vere e proprie risorse, in quanto
per esse, non vengono generate costanti per la classe R e non viene eseguita
alcuna ottimizzazione. L’unico modo per accedere a queste informazioni da
parte dell’applicazione è quello di utilizzare la classe AssetsManager.
Risorse XML: una risorsa XML può essere interpretata dal sistema in modi diversi. Il più semplice prevede di gestire il file come semplice documento contenuto
nella cartella /res/xml.
Risorse raw: si tratta di file contenuti all’interno della cartella /res/raw ai quali
sono associati delle costanti della classe R, ma non vengono ottimizzate. Esempi di questo tipo possono essere file video, audio o comunque file cui si ha
la necessità di accedere secondo una modalità di stream.
35
Capitolo 1
1.4.4
La classe R
Le risorse possono essere usate direttamente all’interno del del codice dell’applicazione usando la classe statica R. Questa classe contiene sottoclassi per ogni tipo
di risorsa definita nell’applicazione. Ogni sottoclasse associa una risorsa ad una
variabile attraverso un identificatore che corrisponde ad una locazione nella tabella
delle risorse.
Tra le classi interne contenute in R si trova la classe layout che contiene a sua
volta una costante intera di nome main. In questo modo, per accedere alla risorsa
main si deve usare il costrutto R.layout.main.
1.5
Activity, Intent e comunicazione tra Activity
Nei seguenti paragrafi si entrerà più in dettaglio nello studio degli elementi fondamentali in Android e cioè, le Activity e gli Intent. Come si vedrà, le Activity forniscono il core delle applicazioni, mentre gli Intent sono utilizzati per far comunicare
le precedenti.
1.5.1
Activity
Ogni schermata da visualizzare sul dispositivo estende la classe Activity. Per utilizzarle è necessario segnalarle nel file AndroidManifest, inserendo tag application,
contenuti a loro volta nei nodi activity.
Tipicamente esiste una schermata principale rappresentata da una Activity di
nome main. Questa schermata è di solito supportata da una o più Activity che
aggiungono informazioni ed interazioni alla prima.
Ciclo di vita di una Activity e metodi di callback
Le Activity sono rappresentate mediante una struttura a stack basata sul principio
last-in-first-out 40 dove l’oggetto in cima alla pila è quello visibile. Attraverso questo
40
Il termine LIFO è l’acronimo di Last In First Out (Ultimo ad entrare, primo ad
uscire) ed esprime il concetto relativo al modo di immagazzinare dati, in cui l’ultimo
valore introdotto è il primo ad uscire.
36
Capitolo 1
stack è possibile determinare lo stato di un’Activity.
Quando una nuova Activity viene avviata questa vine portata in cima alla pila
e quella precedente scala di una posizione. Questo ciclo è illustrato nella seguente
tabella.
Figura 1.6: Activity che si muove nello stack dell’applicazione.
All’interno dello stack ogni attività si può trovare in uno dei seguenti stati:
Active: quando una Activity è in cima allo stack ed è in grado di ricevere gli
input da parte degli utenti, viene posta nello atato Active. Android cerca di
mantenere in vita queste, fermando se necessario quelle a priorità minore.
Paused: caso in cui una Activity è visibile ma non ha il focus. Questo stato si
ha quando Activity superiori hanno un grado di trasparenza, oppure non
occupano tutto lo spazio a disposizione.
Stopped: una Activity non visibile si trova in questo stato; essa rimane in memoria
mantenendo tutte le informazioni, ma è la prima ad essere eliminata in caso
di bisogno.
Inactive: dopo che una Activity è stata eliminata e prima che essa sia nuovamente
lanciata, si trova in questo stato.
37
Capitolo 1
I metodi di callback
Per ognuno dei punti espressi nel paragrafo precedente esiste un metodo definito di
callback, il quale permette l’esecuzione di diverse operazioni. Queste relazioni sono
rappresentate nella figura 1.7.
Il primo metodo nella tabella ha la seguente forma:
protected void onCreate(Bundle savedInstanceState)
Si tratta dell’operazione invocata in corrispondenza della creazione di una Activity,
dove la prima istruzione da eseguire sarà sempre l’invocazione dell’analogo metodo
della classe padre (super.onCreate()). Questa caratteristica è tipica di tutti i metodi
di callback e per non appesantire la lettura si ometteranno ulteriori note a riguardo.
Il parametro di tipo Bundle fornisce un riferimento allo stato che l’Activity aveva prima di essere eliminata dal sistema. Questo parametro può essere salvato
all’interno del metodo onSaveInstanceState() ed essere ripreso nel precedente.
Se il metodo onCreate termina con successo, la Activity viene preparata per la
visualizzazione e il sistema invoca il metodo definito:
protected void onStart()
di ovvio significato.
La successiva funzione dipende dal fatto che la Activity abbia ottenuto o meno
il focus, quindi sia quella in cima allo stack. Se ciò è verificato viene invocato:
protected void onResume()
e se questo termina con successo, la Activity è nello stato RUNNING e può interagire
con l’utente.
Una Activity da rimuovere dalla posizione iniziale dello stack viene messa nello
stato PAUSED e conseguentemente vengono invocati i metodi:
protected void onPause() e protected void onStop()
dove il secondo è eseguito solo nel caso in cui il primo è terminato con successo.
Successivamente si può ripristinare l’attività precedente con:
protected void onRestart()
La logica del metodo onRestart() è molto simile a quella di onCreate(); rispetto
questo metodo, onRestart() deve preoccuparsi di un eventuale ripristino dello stato.
Dopo l’esecuzione di onRestart() vengono invocati rispettivamente onStart() ed
onResume() secondo le stesse modalità viste in precedenza.
38
Capitolo 1
Figura 1.7: Ciclo di vita delle Activity e relativi metodi di callback.
39
Capitolo 1
Il sistema elimina le Activity portandole dallo stato PAUSED in cui l’aveva
lascita a quello di STOPPED invocando il metodo:
protected void onStop()
e poi nello stato INACTIVE con:
protected void onDestroid()
Ciclo di vita di una Activity e metodi di Callback
Il ciclo di vita di una Activity è racchiuso dall’invocazione dei metodi onCreate()
ed onDestroy(). In questa situazione la Activity è visibile, mentre tra i metodi
onStart() ed onStop() è possibile anche l’interazione con l’utente.
Il ciclo attivo invece, è compreso tra onResume() ed il metodo onPause(). Una
Activity in questo stato è in foreground e riceve input da parte degli utenti.
Immediatamente prima del metodo onPause() c’è una chiamata ad onSaveInstanceState() che salva lo stato di una Activity in un oggetto di tipo Boundle, il
quale sarà passato come visto prima al metodo onCreate().
1.5.2
View e Layout
Con l’aumentare della risoluzione degli schermi, della loro grandezza e della potenza
dei processori, sono aumentate le caratteristiche che uno smartphone può fornire dal
punto di vista del layout. Gli sviluppatori android hanno definito delle classi View
41
, dediche alla visualizzazioni e alle interazioni con gli utenti.
Una delle caratteristiche principali delle View è sicuramente legata al fatto che
esse possono essere descritte sia all’interno di documenti XML (all’interno della
cartella /res/layout), o tramite codice, utilizzando le opportune API.
Il metodo più semplice per creare le View è sicuramente la creazione di tag. All’interno di questi sono inseriti molti attributi come andorid:id, android:layout height
ed android:layout width, i quali indicano rispettivamente un identificatore, l’altezza
e la larghezza. Gli ultimi due attributi descritti sono obbligatori e possono con41
Le View non sono l’unico strumento per fornire un layout. Esiste infatti la
classe ViewGroup, estenzione della classe View, la quale è disegnata per contenere
più View contemporaneamente.
40
Capitolo 1
tenere costanti statiche come FILL PARENT
42
o WRAP CONTENT
43
, mentre si
possono usare altri parametri per definire padding, margini ed elementi specifici.
Android mette a disposizione molte generalizzazioni della classe View. Nelle
prossime righe descriveremo quelle più importanti.
LinearLayout: componente base che permette di disporre le View in esso contenute
su una singola riga o colonna, a seconda della proprietà orientation
44
.
RelativeLayout: in questo caso è possibile specificare la posizione assoluta delle
View in esso contenute; sono molto importanti gli attributi descritti dalla
classe RelativeLayout.LayoutParams
45
ed è necessario evitare ciclicità di essi,
all’interno del contenitore.
TableLayout: le TableLayout permettono di organizzare le View secondo una struttura formata da righe e colonne. All’interno dei nodi TableRow è possibile la
definizione di attributi come layout column ed layout span.
FrameLayout: permette il controllo sulla visualizzazione delle View, fornendo strumenti per nasconderle o visualizzarle.
ListView: permettono di visualizzare informazioni attraverso una lista. Questa non
fa altro che implementare righe attraverso il metodo setAdapter( ListAdapter
adapter ), dove ListAdapter è una collezione di Adapter
42
46
.
L’attributo FILL PARENT indica che la View occupa tutto lo spazio disponibile
all’interno del contenitore.
43
L’attributo WRAP CONTENT indica che la View occupa solo lo spazio
necessario alla visualizzazione.
44
Ogni dispositivo è sensibile ai cambiamenti dell’orientamento. Per View
come LinearLayout o RelativeLayout è possibile utilizzare l’attributo andorid:layout orientation, il quale specifica la tipologia di orientazione (portaint o
landscape).
45
Molto importanti sono gli attributi layout toRightOf, layout toLeftOf,
layout alignParentRight, layout alignParentLeft, layout alignBaseline.
46
Gli Adapter permettono di associare dati a determinate View. A seconda della
View associata all’Adapter, esso verrà definito in diverso modo (ad esempio un
Adapter associato ad una ListView è definito ListAdapter). Essi possono essere
assimilabili a collezioni di elementi come le liste, però, al contrario di queste fanno
41
Capitolo 1
GridView: molto simile ad una ListView, però in questo caso si può specificare il
numero di colonne attraverso il quale visualizzare gli elementi.
ExpandableListView: molto utile nel caso in cui si vogliono visualizzare dati che
si differenziano tra loro da una sola caratteristica. Con le ExpandableListView
viene utilizzata la classe ExpandableListAdapter per evidenziare gli attributi
diversi degli elementi che andranno a comporre la lista.
ScrollView: si tratta di una specializzazione della classe FrameLayout che consente
l’inserimento di una sola View figlia (questa però potrà contenere un numero
indefinito di figli), effettuando lo scroll del suo contenuto.
Da notare che una ListView non effettua lo scroll se è all’interno di questo
tag. In questo caso è necessario inserire codici che aggirino l’ostacolo.
Spinner: elemento di selezione univoco, a partire da un elenco scelto dallo sviluppatore.
Gallery: permette la visualizzazione di immagini sullo schermo con scrolling orizzontale. Queste devono essere passate alla View mediante un oggetto di tipo
ImageSpinnerAdapter.
Layout Custom: Android permette la realizzare Layout customizzati attraverso
opportune specializzazioni della classe ViewGroup o dei layout descritti in
precedenza. Come per i Layout, è anche possibile la creazione di attributi
custom.
Widget toolbox
Esse sono utilizzate per creare semplici interfacce che vanno sotto il nome di Widget.
La seguente lista racchiude la maggior parte di essi.
TextView: utilizzate per inserire elementi di sola lettura nella pagina. Supporta
testi multi linea e formattazione di stringhe. La gestione del testo avviene
rifermiento sempre ad un layout che dovrà contenere i dati passati mediante un
Array.
42
Capitolo 1
attraverso overload del metodo setText(), mentre per recuperarlo, si utilizza il
metodo getText().
EditText: elemento che permette l’editazione di un testo.
Button: pulsante standard utilizzato per inviare input al programma.
AutoCompleteTextView e MultiAutoCompleteTextView: entrambe permettono di suggerire parole all’interno di una EditText. Mentre il primo permette
il suggerimento di singole parole, il secondo è dedico a parole all’interno di frasi.
In entrambi i casi è necessario passare all’oggetto le parole su cui effettuare le
ricerche, ad esempio recuperando dati all’interno di un database.
CheckBox: pulsante rappresentato dagli stati checked e unchecked.
RadioButton: anche questo componente è caratterizzato dagli stati checked ed
unchecked. A differenza del precedente non si può deselezionare l’elemento
scelto.
È possibile raggruppare più elementi all’interno di una RadioGroup, dove uno
ed un solo un elemento viene selezionato in ogni istante.
ToggleButton: pulsante che implementa la funzione on/off nel quale è possibile
visualizzare label diverse a seconda dello stato.
CheckedTextView: versione checkable di una TextView. L’utilizzo prevalente di
questa classe avviene nella gestione delle View all’interno delle liste.
I possibili valori della modalità sono descritti dalle costanti CHOICE MODE
NONE (tipologia di selezione default), CHOICE MODE SINGLE (permette di selezionare un solo valore) ed CHOICE MODE MULTIPLE (per la
selezione di un insieme di valori).
ImageView: le classi di questo tipo permettono di reperire un immagine.
AnalogClock e DigitalClock per la visualizzazione di un orologio analogico o
digitale.
43
Capitolo 1
Tutte le precedenti classi derivano dalla classe View. Anche se questa situazione
può essere facilmente intuibile, molto più anomalo è che la classe TextView viene
ereditata da tutte le altre, le quali estendono i suoi metodi.
1.5.3
Intent e comunicazione tra Activity
Come annunciato, gli Intent sono utilizzati in Android per la comunicazione all’interno di una o più applicazioni o come messaggi di sistema. In generale gli Intent
possono essere usati per:
• Dichiarare come le Activity o i Service possono essere azionati per eseguire
una determinata azione.
• Registrare l’avvenimento di un determinato evento.
• Far partire esplicitamente un particolare Service o Activity.
• Inviare messaggi al sistema
Il tipico scenario prevede un componente con la necessità di eseguire una particolare
azione su un determinato insieme di dati. In diversi casi, il componente non sa quale
sarà l’oggetto in grado di soddisfare la sua richiesta, perchè questo dipenderà dall’insieme degli elementi installati nel dispositivo. Ciascuno di questi avrà descritte
le proprie competenze attraverso degli Intent Filter all’interno del file AndoridManifest.xml. È compito del dispositivo decidere in base ad una serie di regole, quale
componente attivare
47
in corrispondenza di un particolare Intent. In questo caso si
parla di Intent implicito.
Un Intent può essere esplicito quando il nome del componente che risponderà è
già noto in fase di creazione dello stesso. La creazione di Intent espliciti può essere
fatta in due modi, a seconda che la Activity da lanciare debba ritornare o meno dati.
Supponendo il caso in cui l’attività portata in cima allo stack non deve ritornare
alcuna notifica a quella di partenza, si utilizza il metodo
public void startActivity(Intent intent)
dove l’Intent può specificare la classe Activity da aprire, può includere un’azione
47
Il processo seguito dal dispositivo, per la determinazione del componente da
attivare a seguito del lancio di una Intent, si chiama Intent Resolution.
44
Capitolo 1
da eseguire o un insieme di dati, espressi mediante URI
48
o dati semplici. Dopo
l’invocazione del precedente metodo è creata una nuova Activity che diviene attiva
e visibile, quindi è mossa al primo posto dello stack. Invocando il metodo finish,
oppure premendo il tasto back del dispositivo, essa è chiusa e rimossa dallo stack.
Esiste un secondo metodo usato quando è necessario per far ritornare dati. In
questo caso si parla di attività padre ed attività figlia, dove viene invocato il metodo:
startActivityForResult(Intent intent, int requestCode)
nel quale, l’Intent è quello da lanciare, mentre requestCode è un intero identificativo
dell’attività chiamata.
Per reperire i dati nell’attività padre si presuppone un meccanismo di recupero.
Esso avviene attraverso l’invocazione di
protected void onActivityResult(int requestCode, int resultCode, Intent data)
dove il requestCode è un intero per distinguere quale attività sta ritornando, mentre resultCode specifica l’esito della chiamata. Se il parametro è impostato a Activity.RESULT OK si indica un esito positivo dell’invocazione, mentre con Activity.RESULT CANCELED si indica che l’operazione non è andata a buon fine, oppure
è stata annullata. Da sottolineare che l’Intent anche in questo caso può ritornare
una URI nel modo definito in precedenza o una collezione di informazioni extra
(mediante la chiamata dei metodi .putExtra(name, value)).
1.5.4
Gestione degli eventi
La gestione degli eventi è uno dei concetti fondamentali riguardanti l’utilizzo dei
componenti di un’interfaccia grafica. Al verificarsi un evento possono eseguirsi determinati codici, quindi, occorrono dei listener in grado di verificare determinati
avvenimenti.
48
Per esempio, si può inviare mediante URI, un numero di telefono che permette
di iniziare una chiamata. In questo caso si utilizza il parametro ACTION DIAL.
Altri tipi di azioni native possono essere: ACTION CALL (inizia immediatamente
una chiamata, usando il numero contenuto nella URI), ACTION INSERT (apre
una Activity per inserire un nuovo elemento nel cursore specificato nella URI dell’intent), ACTION WEB SEARCH (apre una pagina web di ricerca basandola sul
testo inserito nella URI dell’intent).
45
Capitolo 1
Esiste una regola associata al nome di un evento che può essere sintetizzata
come: se Evento è il nome dell’evento l’interfaccia ad esso associata sarà View.
OnEventoListener, la quale definisce metodi di callback. Inoltre, la registrazione
dell’ascoltatore avviene attraverso la definizione di metodi del tipo setOnEventoListener.
Gli eventi di principale importanza sono racchiusi nelle seguenti righe:
OnClickListener: (click) evento di selezione. Dalla versione 1.6 dell’SDK è possibile specificare il metodo da eseguire dopo il click, mediante un attributo, all’interno del documento XML. Questo attributo prende il nome di
android:onClick.
OnLongClickListener: (long click) evento di selezione prolungata di un componente.
OnFocusChangeListener: (focus change) acquisizione o perdita del focus da parte
di un componente.
OnKeyListener: (key) evento di selezione di un tasto.
OnTouchListener: (touch) evento di touch.
Alcuni metodi della classe View vengono invocati automaticamente su tali metodi,
dei quali è possibile farne l’overriding e tutti sono caratterizzati da un parametro
identificatore KeyEvent del tasto premuto. Tra essi si citano:
• onKeyDown
• onKeyUp
• onKeyLongPress
• onKeyPreIme
• onKeyMultiple
• onKeyShortcut
• onKeyUp
46
Capitolo 1
1.6
Dialog, Toast, Widget e Notification
A partire dalle versioni 2.0 della piattaforma sono state inserite importanti novità.
Di questa categoria fanno parte gli home-screen Widget (da non confondere con i
widget relativi alle classi View) ed i notification services.
Oltre i precedenti elementi citati, si tratteranno in questa sezione i Dialog ed i
Toast, i quali sono tutti utilizzati per fornire informazioni all’utente in modo semplice
e veloce.
1.6.1
Dialog
Le finestre di dialogo permettono la visualizzazione e l’inserimento di piccole quantità
di informazioni. Ogni Dialog è caratterizzata da una Activity da cui acquisisce le
informazioni ed è rappresentata mediante i seguenti metodi:
showDialog(int id): per la visualizzazione su schermo, dove id è l’identificativo
della finestra.
onCreateDialog(int id): utilizzato per la creazione della finestra ed eseguito solo
durante la prima richiesta di visualizzazione.
onPrepareDialog(int id, Dialog dialog): permette di preparare la finestra ad
una successiva visualizzazione.
DismissDialog(): un dialog che ha terminato il proprio compito può essere nascosto attraverso questo metodo. Utilizzandolo non vengono effettuate ulteriori
chiamate ad onCreateDialog(int id) per le successive visualizzazioni.
RemoveDialog(): metodo che rimuove la finestra di dialogo. A differenza del
precedente verranno effettuate le chiamate onCreateDialog(int id) per le successive visualizzazioni.
In Android è possibile creare le seguenti finestre di dialogo:
Alert: permettono di segnalare informazioni come errori, warning ecc. Una finestra
di tipo Alert può contenere informazioni circa un titolo ed un messaggio.
47
Capitolo 1
Progress: utilizzate quando si vuole caricare una notevole quantità di dati, oppure
eseguire un’operazione molto lunga. Queste finestre forniscono all’utente l’idea
del caricamento dati.
Date e Time Picker: finestre di dialogo relative ad informazioni su date ed orari.
Custom Dialog: è possibile utilizzare specializzazioni delle AlertDialog o delle
ProgressDialog per le proprie interfacce.
1.6.2
Toast
Un toast è il meccanismo con cui Android mostra dei messaggi temporanei sullo
schermo, in cui non si prevede nessun tipo di interazione con l’utente (se non la
visualizzazione stessa). Per la creazione di questi elementi è necessario dichiarare
una stringa da visualizzare ed una durata.
La durata può assumere i valori descritti dalle costanti LENGTH SHORT,
LENGTH LONG, abedue appartenenti alla classe Toast, oppure un qualsiasi lasso
di tempo espresso in millisecondi.
Per default i Toast vengono visualizzati in basso nello schermo del dispositivo.
Questa ed altre caratteristiche possono essere customizzate.
Essendo questo elemento molto semplice da creare e facile da comprendere, essi
possono essere una valida alternativa alle finestre di dialogo. I produttori di dispositivi mobili che girano su Android, li prediligono nel momento in cui si ha bisogno
di fornire informazioni all’utente.
1.6.3
Notification Service
Questo componente fornisce un meccanismo alternativo per la notificare la presenza
di particolari informazioni. La notifica di questi messaggi avviene nella parte alta
del display detta status bar e non interrompono l’eventuale attività dell’utente, il
quale per visualizzarla dovrà trascinare la barra di riferimento verso il basso.
Le informazioni contenute in una notifica vengono incapsulate all’interno di un
oggetto Notification. Ciascuna Notification contiene:
• un icona.
48
Capitolo 1
• Un breve messaggio da visualizzare ed un layout corrispondente.
• Un Intent da lanciare nel caso di selezione.
Oltre a le informazioni obbligatorie è possibile associare un particolare un suono
una vibrazione
1.6.4
50
, l’attivazione dei LED
51
49
,
.
Home-screen Widget
I Widget e più specificatamente le AppWidgets sono componenti visuali rappresentati nella home del dispositivo. Un Widget è creato per la visualizzazione di elementi
come appuntamenti, messaggi inviati o informazioni riguardanti il meteo. Spesso i
progettisti di applicazioni forniscono diverse versioni dei Widget, che visualizzano le
stesse informazioni, in modi diversi e con diversa grandezza
52
.
Questi elementi sono implementati come IntentReceivers ed utilizano RemoteViews
53
per visualizzarne il contenuto. Per la loro creazione è necessario inserire
tre componenti:
1. Un layout che definisce la UI del Widget.
2. Un file XML che definisce i metadata associati.
49
Per aggiungere un suono alla notifica è necessario modificare un attributo pubblico, nel caso si voglia utilizzare un suono di default, oppure creare un riferimento
ad un media da riprodurre mediante URI per la riproduzione di un suono specifico.
50
Anche per la vibrazione bisogna modificare un attributo pubblico, dove il corrispondente valore è un Array di long i quali rappresentano, in millisecondi, il dalay
relativo alla prima vibrazione, la sua durata, ed eventuali coppie di informazioni
dello stesso tipo, relativamente a delay e durata di vibrazioni successive alla prima.
51
In questo caso è possibile scegliere il colore del LED, la durata relativa
all’accenzione e allo spegnimento.
52
La home-screen di un dispositivo Android è divisa da una griglia 4x4, dove
ognuna di esse ha grandezza prefissata di 74x74 dp (device-indipendent pixels).
Per scegliere il numero dei bolcchi che il widget occuperà è necessario effettuare il
seguente conto: Mininum size dp= (Cell-coun * 74dp) 2dp. Nel caso il numero
non corrisponda al precedente conto vengono utilizzate un numero di celle che più
si avvicina ad esso.
53
RemoteView: classe che descrivere una gerarchia di view che possono essere
visualizzare in un processo separato. La gerarchia è dichiarata attraverso un file
XML e questa classe racchiude operazioni base per modificare il contenuto.
49
Capitolo 1
3. Un Intent Receiver che definisce i controlli.
4. Un periodo minimo per l’aggiornamento espresso in millisecondi, oppure un’attività di configurazione.
5. Uno specifico tag all’interno del file AndroidManifest.xml.
Anche se un Widget è definito attraverso un file XML creato ad-hoc, non è
possibile inserire tutte le View messe a disposizione in Android.
Attualmente i Layout utilizzabili sono limitati a:
• FrameLayout
• LinearLayout
• RelativeLayout
Le View utilizzabili sono invece:
• AnalogClock
• Button
• Chronometer
• ImageButton
• ImageView
• ProgressBar
• TextView
1.7
Gestione dei dati
Il salvataggio ed il caricamento dei dati è una caratteristica essenziale per molte applicazioni. Le tecniche utilizzate in android per questo tipo di operazioni si dividono
in:
50
Capitolo 1
Shared Preferneces: permettono il salvataggio di elementi primitivi (String, Long,
Boolean, Float ed Integer ) mediante una coppia key/value che prende il nome
di preferenza.
Per creare una Shared Preference è necessario chiamare il metodo getSharedPreferences passando il nome della stessa, mentre per modificarla è inoltre
necessario effettuare chiamate ai metodi edit() e commit().
Una volta aperta la preferenza è possibile operare con i dati in essa contenuti
attraverso i metodi get()
54
e put()
55
.
Application State: ogni Activity include come visto in precedenza determinati
eventi atti al salvataggio dello stato corrente. In questo caso si utilizzano
oggetti Bundle in cui possono essere passati parametri primitivi rappresentati
da una coppia key/value.
Files: permettono il salvataggio di dati su file. Come avviene in Java è possibile
aprire stream in lettura e scrittura attraverso i metodi openFileInput(String
name) ed openFileOutput(String name, int mode), dove si specifica il nome
del file e nel caso di scrittura, si decide la visibilità dello stesso da parte di
altre applicazioni. Un valore corrispondente a MODE PRIVATE della classe
Context permette di specificare che il file è accessibile solo all’applicazione
che lo crea. Un valore impostato a MODE WORLD READABLE permette la
lettura ad altre applicazioni, mentre MODE WORLD WRITEABLE permette
anche la scrittura.
Casi d’uso sono principalmente i seguenti:
54
Tra i metodi get di una shared preference troviamo: getBoolean(String key,
Boolean defaultValue), getFloat(String key, Float defaultValue), getInt(String key,
Integer defaultValue), getLong(String key, Long defaultValue), getString(String key,
String defaultValue). In tutti questi casi i parametri hanno lo stesso significato.
Il primo è utilizzato come chiave della preferenza, mentre il secondo imposta il
valore di essa ad uno di default, scelto nel caso in cui non venga trovata la chiave
corrispondente.
55
Allo stesso modo dei metodi get() è possibile utilizzare i metodi put() sostituendo
get a put. Gli attributi da passare assumono lo stesso significato del caso precedente.
51
Capitolo 1
• Lettura o scrittura su filesystem locale. Le modalità di accesso mofica e
creazione sono quelle descritte in precedenza.
• Lettura o scrittura di informazioni su SD card. I procedimenti di lettura
e scrittura sono ugauali a quelli su filesystem locale. L’unica differenza è
nella directory in cui tali memorie vengono montate. In queste situazioni
è molto utilizzato il metodo:
String extStDir = Environment.getExternalStorageDirectory().toString()
il quale restituisce una stringa contenente la posizione della directory.
• Lettura di un file statico che si trova all’interno della cartella res/raw.
In questa cartella sono inseriti file a cui non si vuole applicare nessun
processo di ottimizzazione, ma caratteristici dell’identificatore della classe
R.
Per utilizzare questa tipologia di risorse è necessario utilizzare il metodo:
openRawResource(int id)
il quale restituisce un oggetto di tipo InputStream.
Content Providers: i Content Providers offrono uno strumento ben definito per
usare e diffondere dati. Essi sono rappresentati attraverso una semplice URI,
56
, del tipo content://schema.
Molti database nativi 57 , come contatti telefonici o media store, possono essere
utilizzati dai Content Providers per l’accesso, la modifica e l’eliminazione, dei
dati in esso contenuti.
56
È buona norma utilizzare il path del progetto nella URI nel seguente modo content://com.CompanyName.provider.ApplicationName/DataPath. Allo stesso modo
la dicitura /RowNumber è rappresentata per effettuare una singola richiesta. Ancora
una volta troviamo l’associazione tra una URI ed un mime-type dei corrispondenti
elementi. Un mime-type è composto da una parte type ed una parte subtype (ad
esempio un mime-type potrebbe essere image/jpeg), dove la prima parte esprime un
contenuto e la seconda, dipendente dalla prima, una descrizione della modalità di
rappresentazione del dato.
57
Esempi di Content Providers nativi possono essere UserDictionary (contenente i
dati relativi al dizionario utente), Setting (conentente le preferenze del dispositivo),
MediaStore (gestisce l’accesso ai dati multimediali quali audio, video ed immagini),
Contacts (per la gestione dei contatti dell’utente) e CallLog (gestore delle chiamate).
52
Capitolo 1
Database: una delle caratteristiche nella progettazione di applicazioni Android
ed in generale per qualsiasi piattaforma o linguaggio è la gestione di dati
attraverso un database.
Esistono molte tecnologie per la creazione di base dati, ma quella che ha avuto
maggior successo fa uso di uno schema relazionale. Google ed altri produttori
di sistemi per dispositivi mobili hanno implementato un RDBMS all’interno
dello stack, un database embedded chiamato SQLite.
Attraverso delle reali necessità, negli ultimi anni sono apparse nuove tecnologie
per la memorizzazione dei dati all’interno di un database. Queste vengono
comunemente chiamate OODBMS [10] oppure ODBMS dove O sta per Object.
Una buona parte di questa tesi è dedica allo studio di queste tecnologie, si
rimanda allo specifico capitolo per approfondire i concetti.
53
Capitolo
2
Database ad oggetti e relazionali
In molte applicazioni è richiesta la gestione permanente dei dati. L’informatica ha
inizialmente messo a disposizione un sistema per gestione delle informazioni mediante dei semplici archivi poco flessibili e performanti. Il programmatore aveva la
completa responsabilità circa l’organizzazione e gestione dei dati, il che portava ad
una grande ridondanza di dati, spreco di memoria e rischio di inconsistenza degli
stessi.
In un secondo momento a questi semplici archivi si sono aggiunti sistemi più
potenti chiamati banche dati o base dati. Un database è un insieme di dati permanente organizzato per minimizzare la ridondanza e per essere accessibile da parte di
più utenti ed applicazioni.
Insieme alle basi di dati si citano i DBMS. Un DBMS (Data Base Management
System) è un sistema software progettato per consentire la creazione e manipolazione
di database. Se in passato i DBMS erano diffusi principalmente presso le grandi
aziende e istituzioni, oggi il loro utilizzo è diffuso in ogni contesto.
Un DBMS può essere costituito da un insieme complesso di programmi che controllano l’organizzazione, la memorizzazione e il reperimento dei dati in un database.
Esso controlla anche la sicurezza e l’integrità della base di dati, non consentendo a
più utenti di modificare lo stesso record contemporaneamente mediante il blocco dei
record.
I linguaggi di interrogazione del database permettono agli utenti di interrogare il sistema e di analizzarne i dati mediante delle query. Molti DBMS support54
Capitolo 2
ano le API (Application Programming Interface) dell’Open Database Connectivity (ODBC) o Java Database Connectivity (JDBC, lo standard per Java), le quali
forniscono strumenti standardizzati per l’accesso ai database.
2.1
Database Relazionali e RelationalDBMS
Nel 1970 si cominciò a produrre diversi documenti culminati nel Modello Relazionale.
Si descrisse un nuovo sistema per archiviare e modificare grandi quantità di dati
mediante l’uso di righe (record o anche tuple) e tabelle.
Il modello relazionale si basa sulla nozione di relazione appartenente alla teoria
degli insiemi. Per ogni record
1
viene definita una chiave 2 , ovvero un identificatore
univoco della tupla. La chiave può essere qualunque dato memorizzato, un campo
aggiunto specificatamente per questo scopo, o una combinazione di più campi (chiave
composta).
In questo tipo di rappresentazione l’ordinamento di righe e colonne è irrilevante.
Inoltre, nel caso in cui il valore non è disponibile su una n-pla si utilizza la nozione
di valore nullo (NULL), il quale può generare problemi nel caso in cui il campo sia
importante per la tabella e per questo motivo esistono specifiche per l’inserimento
forzato degli elementi.
Esistono linguaggi per effettuare operazioni di inserimento, modifica, ed interrogazione. Questi rientrano sotto il nome di: DDL 3 , DML
1
4
e QUERIES 5 , i quali
Nelle basi di dati relazionali l’elemento corrispondente al record è chiamato
tupla.
2
Una chiave è l’elemento di riferimento che distingue una riga da un’altra. Una
chiave può essere interna, se appartiene alla stessa tabella, o esterna, se si fa riferimento ad un campo relativo ad una tabella diversa da quella attuale. Di solito si fa
uso di un ID per la creazione di una chiave int.
3
DDL: Data Definition Language. I database relazionali utilizzano istruzioni del
tipo CREATE TABLE, che permettono la definizione di una tabella all’interno dello
schema.
4
DML: Data Manipolation Language; consente di leggere, inserire, modificare o
eliminare i dati in un database. I comandi DML esprimono azioni da effettuare sui
dati identificati dalla parola iniziale dell’istruzione che quasi sempre è un verbo. Nel
caso di SQL i verbi utilizzati sono SELECT, INSERT, UPDATE e DELETE.
5
QUERIES: Interrogazioni al database effettuate mediante il costrutto SELECT55
Capitolo 2
sono spesso associati ad SQL 6 .
Il modello relazionale rappresenta oltre il 90% della tecnologia aziendale riguardo
i DBMS. Il successo di questo sistema è dovuto alla sua solidità, al suo linguaggio
SQL e al fatto che è stato il primo ad entrare nel mercato globale; le aziende che hanno costruito il proprio DB mediante uno schema relazionale non vedono la necessità
di cambiare modello.
Al giorno d’oggi molti programmi sono spesso costruiti con l’aiuto della tecnologia ad oggetti e database relazionali. Un sistema ad oggetti è caratterizzato da
identità, stati, ecc, mentre il modello relazionale è caratterizzato da tabelle, colonne,
righe e chiavi. In questo caso diviene necessario un sistema intermedio detto ORDBMS
8
7
che introduce un nuovo livello nello stack software, dettato da un ORM
.
2.1.1
SQL
SQL [8] è un linguaggio progettato per leggere, modificare e gestire dati memorizzati
in un sistema di gestione di basi di dati, basato sul modello relazionale. La prima
versione fu sviluppata da IBM all’inizio degli anni settanta e in quelli successivi sono
state rilasciate altre versioni riconosciute dall’ISO 9 .
FROM-WHERE.
6
SQL: Structured Query Language. Questo sistema si basa su un solido modello
matematico, ma al tempo stesso è di facile comprensione.
7
ORDBMS: object-relational mappers. Oggi molte applicazioni mischiano il
modello relazionale e il modello a oggetti; si parla quindi di ORDBMS o Object Relational DBMS. Naturalmente si può utilizzare un programma procedurale con un
modello relazionale. Bisogna ammettere che questa soluzione è abbastanza obsoleta,
quindi si esula dal descrivere eventuali vantaggi o difetti della stessa.
8
ORM o Object-Relational Mapping. È una tecnica di programmazione che
favorisce l’integrazione di sistemi software orientati agli oggetti con sistemi RDBMS.
Tra gli ORM si possono citare Hibernate o Tolpick; questi sistemi incrementano la
complessità e la latenza del sistema.
9
L’Organizzazione internazionale per la normazione (ISO o International Organization for Standardization); è la più importante organizzazione a livello mondiale
per la definizione di norme tecniche. Le norme ISO sono numerate e hanno un formato del tipo ISO 99999:yyyy: Titolo dove 99999 è il numero della norma, yyyy
l’anno di pubblicazione e Titolo è una breve descrizione.
56
Capitolo 2
La maggior parte dei sistemi per la gestione di database implementano questi
standard ed aggiungono funzionalità proprietarie. In questo modo si creano dialetti
che si differenziano, seppur minimamente da altri.
SQL progettato originariamente come linguaggio di tipo dichiarativo, si è successivamente evoluto con l’introduzione di costrutti procedurali, istruzioni per il
controllo di flusso, tipi di dati definiti dall’utente e varie altre estensioni.
Lo standard si divide in:
Data Definition Language: (DDL) permette di creare e cancellare database o di
modificarne la struttura.
Data Manipulation Language: (DML) usato per aggiungere inserire e manipolare i dati.
Data Control Language: (DCL) usato per autorizzare gli utenti o un gruppo di
essi ad accedere ai dati.
Query language: (QL) permette di interrogare il database.
Tra i costrutti fondamentali appartenenti alle precedenti categorie si citano:
• CREATE TABLE: per la creazione di una tabella dei campi e degli id in essa
contenuti.
• ALTER TABLE: per modificare la struttura di una tabella (aggiungere, rimuovere e modificare un campo).
• DROP TABLE: per la cancellazione di una tabella.
• INSERT: inserimento dei dati in una tabella.
• SELECT: ricerca di campi all’interno di una singola tabella. A questo costrutto si associa la clausola FROM (tabelle cui cercare i campi) e WHERE (clausole
di selezione).
• JOIN: utilizzato per cercare campi all’interno di più tabelle. Questa tecnica è
in grado di fondere le precedenti.
57
Capitolo 2
Come un qualsiasi linguaggio di programmazione, anche l’SQL utilizza una serie di
simboli atti a definire uguaglianze, fare confronti e calcoli mediante operatori. Gli
operatori messi a disposizione dal SQL standard si dividono in quattro categorie:
• Operatori di confronto;
• Operatori aritmetici;
• Operatori condizionali;
• Operatori logici;
2.1.2
SQLlite
Una delle caratteristiche di Android riguarda la possibilità di gestire i dati attraverso un database relazionale. Si tratta di SQLlite, un DBMS che ha tra le sue caratteristiche principali quella di essere molto leggero (circa 500kb), veloce, semplice
ed open-source. Essendo adatto per dispositivi a risorse limitate, è stato implementato nello stack Android (oltre che a molti dispositivi elettronici, incluso molti
riproduttori MP3, iPhone ed iPod).
In Android ogni database SQLite [9] è parte integrante dell’applicazione a cui
si riferisce; gli strumenti per la gestione di SQLlite si possono dividere nelle classi
usate per la gestione dei cursori
10
e in quelle per la gestione delle informazioni. È
inoltre possibile associare un insieme di DB-SQLite alla stessa applicazione.
Il database è integrato in un singolo file, non sono necessarie configurazioni e non
serve una connessione client-server per interrogare il DB (questa non è nemmeno
possibile). Al contrario della maggior parte dei RDBMS, non è stato implementato
l’uso delle sintassi SQL come: ALTER TABLE, TRIGGER e VIEW (viste).
È possibile utilizzare un metodo per la creazione del database e dichiarare se è
apribile solo in lettura (OPEN READONLY ), oppure se è possibile accedervi anche
in scrittura (OPEN WRITE ). In alternativa si può creare un database in memoria
10
Un cursore definisce un insieme di operazioni che si possono classificare in: operazioni di movimento le quali permettono di posizionare il cursore in varie posizioni,
ed operazioni di controllo che permettono di fare test sui dati estratti. Infine ci sono
le operazioni che permettono l’accesso ai dati contenuti nel cursore stesso.
58
Capitolo 2
senza crearne il rispettivo file, di modo che, il DB è cancellato al momento della
chiusura dell’applicazione di riferimento.
La creazione delle tabelle avviene allo stesso modo con cui si creano in un normale
database relazionale, cioè con l’esecuzione delle classiche istruzioni SQL del tipo
CREATE TABLE seguito dalle chiavi, dagli eventuali indici ecc.
Per i database creati con SQLlite è strettamente raccomandato aggiungere una
chiave identificativa con l’attributo autoincrement
11
.
Classe SQLiteDatabase
la classe SQLiteDatabase mette a disposizione gli strumenti per effettuare le query
che si possono classificare in:
• ExecSQL: in grado di eseguire script SQL di vario tipo purchè non di query
(SELECT ).
• Delete: permette di eliminare dati a partire dal nome della tabella e l’eventuale
clausola WHERE con i rispettivi valori.
• Insert: permetterne l’inserimento di un nuovo elemento all’interno della tabella.
• Update: prevede in genere un oggetto di tipo ContentValues
12
contenente
i nuovi valori da aggiornare e le eventuali nuove informazioni relative alla
clausola WHERE.
• Replace: usata nel caso in cui si ha la necessità di sostituire completamente i
valori di un insieme di righe.
• Query: insieme di API che permettono l’estrazione dei dati dalle tuple. Il
risultato di una operazione di query fa riferimento ad un oggetto Cursor, il
quale permette di scorrere le informazioni restituite.
11
L’attributo autoincrement indica che ad ogni nuova tupla inserita si inserisce un
nuovo identificatore, in modo del tutto trasparente all’utente.
12
Per inserire i dati si utilizza un oggetto di tipo ContentValues. Il metodo put
del ContentValues prende in ingresso il nome del valore (String key) e il valore stesso
(String value); l’oggetto ContentValues è in pratica è una mappa che associa al nome
di una colonna, il rispettivo valore utilizzato per il passaggio dei valori.
59
Capitolo 2
Classe SQLiteQueryBuilder
Spesso si ha la necessità di creare query dinamiche il cui insieme di colonne, di clausole where o criteri di ordinamento, non sono noti al momento della compilazione.
In questi casi viene utilizzata la classe SQLiteQueryBuilder, la quale permette di
incapsulare in un unico oggetto la logica di creazione delle query.
2.2
ODBMS ed RDBMS
Supponiamo di aver creato un programma con un linguaggio orientato ad oggetti,
ad esempio Java. Questo programma ha un numero di oggetti da dover salvare
all’interno di un qualsiasi database.
Assumendo di aver creato una semplice classe da memorizzare in uno schema
relazionale, si deve creare codici SQL per la creazione delle tabelle. Allo stesso
modo si dovranno scrivere molte righe di codice per effettuare query, upload ed
eliminazioni.
Il precedente progetto potrebbe essere implementato utilizzando la teoria degli
ORDBMS. In questo caso lo sviluppatore non deve gestire il lavoro di traduzione di
una tupla in un oggetto. Infatti, questo avviene in modo semi-trasparente. L’unico
compito lasciato allo sviluppatore è la gestione della traduzione di una tabella in un
oggetto (che di solito avviene mediante file XML).
La precedente ipotesi ha dei limiti dettati dai tempi di latenza richiesti per
effettuare il mapping e dalla gestione dello stesso. Gli sviluppatori si sono chiesti
perciò se tutti questi problemi potevano essere risolti. Il risultato fu la creazione dei
“Pure Object Database”. Con questa soluzione, oltre a non dover effettuare nessun
tipo di traduzione tra modello relazionale e quello ad oggetti, non si deve creare
nessuna struttura per il database, poichè essa è definita direttamente dalle relazioni
delle classi.
Esistono molti database ad oggetti e molti di essi sono caratterizzati dalla licenza
open-source. Lo scopo della tesi è quello di associare una base di dati ad un’applicazione Android, quindi si può restringere il cerchio, cercando sistemi che non hanno
bisogno di una connessione client-server per operare con i dati in esso contenuti. Il
60
Capitolo 2
risultato evidenzia Db4o [11] [12] prodotto dalla Versant e Perst [15] prodotto da
McObject.
2.3
DBMS ad oggetti
Molti sviluppatori hanno conoscenza dei database. La maggior parte di essi hanno
familiarità con la programmazione orientata agli oggetti ed hanno necessità di utilizzare un database per mantenere la persistenza. Utilizzando le nozioni introdotte
in precedenza si deve effettuare un mapping tra un sistema ad oggetti verso un sistema relazionale. Esther Dyson
13
ha espresso la seguente affermazione per fare un
paragone tra Relational Database ed Object Database:
Using tables to store objects is like driving your car home and then disassembling
it to put it in the garage. It can be assembled again in the morning, but one eventually
asks whether this is the most efficient way to park a car.
In casi in cui la struttura del progetto è costituita solo da un elemento, potrebbe
essere semplice convertire l’oggetto in una tabella di un database relazionale. Sfortunatamente non è sempre cosı̀ semplice; potrebbero esserci oggetti che hanno riferimenti ad altri o collezioni di essi. In casi come questi risulta difficile creare tabelle
a partire dai precedenti.
Questo problema può essere eliminato mediante l’utilizzo di ODBMS
13
14
. Gli
Esther Dyson (Zurigo, 14 luglio 1957) è una giornalista statunitense nota esperta
campo delle tecnologie digitali emergenti. Dyson e la sua azienda sono specializzati
nell’analisi dell’impatto delle tecnologie informatiche, nei mercati, sull’economia e
nella società.
14
ODBMS: Object Database Management System. Nel 1989 all’interno della pubblicazione “The Object-Oriented Database System Manifesto”, Malcom Atkinson e
Francois Bancilhon definirono un ODBMS come un sistema di basi di dati orientato
agli oggetti che deve soddisfare due criteri: deve essere un DBMS e un sistema orientato agli oggetti. Il primo criterio si traduce in 5 richieste: persistenza, gestione
del salvataggio dati, concorrenza, recovery e creazione di query ad hoc. Il secondo
requisito si traduce in in sette affermazioni: oggetti complessi, identità degli oggetti,
incapsulazione, tipi di classi, estensibilità, overriding, ereditarietà.
La strada degli ODBMS però è ancora più lunga, infatti inizia nel lontano 1980 con
il progetto ORION che porterà alla nascita di ITASCA e Versant.
Per avere il primo progetto Open-Source si dovrà aspettare il 2004 (Db4o).
61
Capitolo 2
Figura 2.1: Sistema ORM, messo a confronto con un database ad oggetti (Db4o in
particolare).
ODBMS non sono una nuova tecnologia poichè sono stati implementati nelle applicazioni fin dall’inizio degli anni 90, ma non hanno avuto almeno inizialmente una
grande notorietà. A seguito della creazione di questi sistemi è stato fondato l’ODMG
15
che ha cercato di stabilire le regole per i database ad oggetti, quali ODL
ed OQL
17
16
. Questi standard sono stati adottati con molte estensioni, ma non sono
stati mai presi come regole da seguire. La versione finale dello standard ODMG,
ODMG 3.0, venne rilasciata nel 2001, ma il gruppo si è sciolse poco dopo.
Mediante l’utilizzo di database ad oggetti, lo schema del database è rappresentato dalle stesse classi. Gli oggetti sono salvati esattamente nello stesso modo in cui
essi sono creati all’interno dell’applicazione. In questa struttura è importante mantenere le relazioni delle classi all’interno del database, ed al contrario dei database
15
ODGM: Object Database Management Group. L’ODMG è stato concepito nell’estate del 1991 da fornitori di database ad oggetti. Nel 1998 l’ODMG ha cambiato
il suo nome, in modo da riflettere le specifiche di database ad oggetti e relazionali
ad oggetti (prodotto di mapping). Tra il 1993 e il 2001 la ODMG ha pubblicato
cinque revisioni.
16
ODL: Object Data Language. Linguaggio per la definizione di oggetti
equivalente al linguaggio DDL di strutture relazioniali.
17
OQL: Object Query Language (linguaggio standardizzato per le query). Con
l’OQL è possibile effettuare query in modo del tutto simile all’SQL, mantenendo strutture base SELECT-FROM-WHERE, ma eliminando INSERT DELETE ed
UPDATE. L’idea di creare query in questo modo venne abbandonata nel 2001 e
nel 2005, venne proposto di creare query mediante il linguaggio di programmazione
utilizzato.
62
Capitolo 2
Figura 2.2: Visione degli OID, relativi agli oggetti, mediante il tool Object Manager.
relazionali è possibile l’inserimento di relazioni inverse, a cui daremo spazio in un
paragrafo dedicato.
Un oggetto è salvato mediante una chiave univoca OID
18
. Questa chiave fa in
modo che ogni oggetto sia univoco rispetto agli altri, in modo da poter creare oggetti
identici ma distinti fra loro. L’identificatore è solitamente un numero e non è visibile
agli utenti ed agli sviluppatori.
Anche se esistono molte analogie tra il concetto di ID tipico dei modelli relazionali
e quello OID, esistono molte differenze tra di loro. Mentre una chiave è creata,
visibile e può essere modificata dagli utenti, un OID non è modificabile ed è un
concetto del tutto trasparente all’utente. L’unicità di una chiave concerne solamente
le relazioni a cui essa riferisce, mentre gli OID permettono di identificare una singola
istanza all’interno di tutta la base dati. In questo caso si potrebbe incorrere in
problemi poichè è possibile creare e salvare duplicati di oggetti che già esistono nel
database. Questo problema è risolto lasciando allo sviluppatore la scelta di poter
aggiungere oggetti identici, mediante controlli effettuati via codice.
18
OID: Object ID. Chiave univoca all’interno del database. Essa è utilizzata per
mantenere le relazioni tra gli oggetti in modo del tutto trasparente all’utente e allo
sviluppatore. Anche per le relazioni inverse si fa uso degli OID.
63
Capitolo 2
Figura 2.3: semplice struttura ad albero di una base ad oggetti, contentente Piloti,
Scuderie ed Automobili.
L’intera base dati può essere vista sotto forma di un grafo o di un albero in
cui, i nodi sono costituiti dagli oggetti rappresentati, mentre gli archi indicano i
collegamenti esistenti. Lo spostamento da un nodo all’altro è indicato con il termine
navigazione o attraversamento. La navigazione è il modo con cui si effettua l’accesso
all’interno di una base dati orientata agli oggetti: ci si sposta da un oggetto verso
un’altro a lui direttamente connesso fino a giungere al nodo foglia (finale).
In questi ultimi anni molte aziende stanno avendo un occhio di riguardo verso gli
OODBMS. Le applicazioni che traggono i maggiori vantaggi dall’utilizzo di questo
approccio sono quelle che gestiscono relazioni complesse tra oggetti
19
.
Benefici di questo modello vanno dall’eliminazione di molte linee di codice per
accedere ai dati (Query, Insert ed ORM), alla riduzione o eliminazione di configurazioni.
19
La letteratura scientifica enfatizza sulle performance delle basi dati ad oggetti:
viene affermato che su dati complessi, un DBMS orientato agli oggetti si dimostra
dal 10% al 100% più veloce di un RDBMS. L’ampiezza di tale intervallo viene
giustificata dal diverso grado di complessità del dato memorizzato.
64
Capitolo 2
2.4
Db4o
Db4o
20
è un prodotto sponsorizzato dalla Versant Corporation
21
utilizzato in più
di 170 paesi in tutto il mondo, con una comunità composta da più di 60.000 membri.
La missione degli sviluppatori è fornire un’alternativa all’approccio relazionale.
Db4o è nato nel 2004, principalmente utilizzato per dispositivi mobili e per progetti che girano su Java o .NET. Tra le industrie che usano db4o si possono citare
aziende che si occupano di comunicazione, scienze mediche e trasporti. Tra le principali industrie ci sono: Boeing, Bosch ed Intel. Molto frequentemente queste industrie
scelgono il database Versant nel caso in cui si devono gestire oltre 10GB
22
di dati.
Altra caratteristica importante riguarda il fatto che db4o a discapito di altri DBMS embedded consente la modalità client-server
23
, oltre ad un sistema di
replicazione DRS (database replication system) verso un server a modello relazionale.
Db4o è basato da una singola libreria
24
facile da implementare in un’appli-
cazione, sia essa basata su Java oppure su .NET. Il suo successo sicuramente dovu20
Db4o è stato creato dalla Db4object inc., fondata nel 2000 da Carl Rosenberger.
Qualche anno dopo è stato rilasciato il primo prodotto: db4o 1.0 per Java. Nel 2004
la compagnia è stata incorporata sotto la CEO Christof Wittig.
21
La Versant Corporation è sita nella città di Redwood, California. Versant è leader nel campo dei database ad oggetti, supportando tecnologie open-source e commerciali. Tra i progetti open-source spicca sicuramente Db4o, mentre la soluzione
commerciale adatta ad applicazioni destkop, è stata chiamata VOD (Versant Object
Database).
22
È stato provato che db4o può essere utilizzabile con database fino a 254GB. Se
il database cresce oltre questo punto si possono creare nuovi file. Questa soluzione
può eliminare il limite di spazio, ma in questi casi è consigliabile l’uso di database
relazionali, i quali hanno limiti di dimensione ben superiori.
23
Molte applicazioni interagiscono con il database mediante un modello client/server. Con l’utilizzo di Db4o questo non è necessario. Le ragioni di questa scelta
sono sicuramente da imputare all’utilizzo prevalente in client standalone che non
consentono connessione verso nessun server. Db4o non è stato il primo sistema ad
introdurre questa caratteristica, esistono infatti molti database relazionali che fanno
uso della stessa modalità.
24
Esistono tre diverse versioni di Db4o; in tutti i casi bisogna includere all’interno
del progetto una libreria di riferimento denominata con il nome di core. In alternativa è possibile includere nel progetto solo determinate classi diminuendo la memoria
occupata.
65
Capitolo 2
to alle elevate performance, agli zero costi di amministrazione ed alla sua elevata
semplicità. Altro punto fondamentale sta nella sua versione open-source che ha
nettamente aumentato il successo di questo sistema.
Per tutte le caratteristiche esposte in precedenza, Db4o è una valida alternativa
ad SQLite per la gestione di database all’interno delle apps Android. Come nel caso
di SQLite, il file del database è incluso nella directory data/data/package-name/ in
un singolo file con estensione .db4o. In alternativa si può utilizzare l’estensione .yap
(yet another protocol), ma è consentito utilizzare qualsiasi altra estensione.
2.4.1
Licenza
La doppia licenza dei prodotti sta diventando uno dei modelli fondamentali per
i progetti software delle aziende, mantenendo in questo modo le potenzialità di
ognuna delle caratteristiche open-source e closed-source. È importante capire cosa
permettono questi modelli per evitare di incorrere in violazioni, specialmente per le
licenze open. Di solito per il prodotto open si usa la licenza GNU
25
.
La Versant offre tre differenti licenze:
General Public Licence (GPL) verrsione 3: il prodotto è liberamente utilizzabile per questa licenza sotto le specifiche della licenza GPL.
Commercial License: questa licenza a discapito della GPL, include opzioni premium, come il supporto tecnico 24/7, ed inoltre è possibile contravvenire alle
richieste necessarie nella licenza GPL.
Db4o Opensource Compatibility Licence (dOCL): utilizzabile per progetti a
libero dominio che implementano db4o, ma che non rientrano nelle specifiche
GPL o commerciale.
25
GPL: General Public License (version 2). La GNU General Public License è
una licenza per software libero. È comunemente indicata con l’acronimo GNU GPL
o semplicemente GPL. La GNU GPL assicura all’utente libertà di utilizzo, copia,
modifica e distribuzione.
66
Capitolo 2
2.4.2
Object Container ed Operazioni basilari
L’Object Container può essere considerato come la porta di accesso al database.
Normalmente si deve aprire la connessione quando l’applicazione inizia, chiudendola
nel momento in cui essa termina.
Le operazioni basilari includono il caricamento, la modifica, l’eliminazione e le
query sugli oggetti. Essi sono trattati nel dettaglio nelle prossime righe.
Apertura Database: per aprire il database non si deve inserire nessuna istruzione.
L’unica condizione richiesta è il passaggio all’Object Container del file-name
relativo al database in questione.
Chiusura Database: è molto importante la chiusura dell’ObjectContainer attraverso il metodo close(). Questo dichiara la chiusura di una connessione aperta.
Chiudendo la transizione si scrivono gli oggetti creati o modificati nel database
nella sessione corrente.
Caricare oggetti: caricare oggetti nel database è estremamente facile. Bisogna solo passare l’oggetto da salvare attraverso il metodo store(). Utilizzando questo
metodo i dati non sono scritti nel database immediatamente, ma saranno scritti
alla fine della transizione.
Delete: per eliminare un oggetto si utilizza il metodo delete(), cui passare l’oggetto
da eliminare. Attualmente db4o non verifica se un oggetto che si intende
eliminare sia referenziato con un altri, per cui è compito dello sviluppatore
effettuare questa verifica.
Update: in questo caso bisogna recuperare gli oggetti, modificarli mediante i metodi setxxx() caratteristici delle classi, ed infine utilizzare nuovamente il metodo
store().
Se si vuole effettuare una modifica è necessario effettuare operazioni di recupero e modifica nella stessa transizione. In caso contrario viene inserito un
nuovo elemento nel database, il che porta ad un grave errore.
67
Capitolo 2
Opzioni: prima dell’apertura dell’Object Container è possibile dichiarare una serie
di configurazioni, come la stampa della versione del database, la creazione di
indici
26
, ecc.
Index: come per la maggior parte dei RDBMS, Db4o supporta gli indici aggiungendo una voce per ogni oggetto. In questo modo operazioni di inserimento e
modifica sono leggermente più lente, ma le query sono altamente preformanti
27
.
Tipi primitivi come interi, long e double, possono essere indicizzati. In casi
come questo le query raggiungono prestazioni significative. In altro modo
possono essere indicizzati stringhe, DateTime, DateTimeOffset, BigDecimal
e BigNumber allo stesso modo dei tipi primitivi. Non posso essere invece
indicizzati Array e collezioni.
2.4.3
Query
Probabilmente la caratteristica fondamentale dei database è quella di poter reperire
dati da esso. La capacità di effettuare query è fondamentale per il successo di una
base dati.
Il modo di definire le query può essere assimilato al modello di dati; i database
relazionali ad esempio sono sempre associati al loro linguaggio SQL
28
. Essendo
Db4o un sistema ad oggetti ci si rende facilmente conto che è necessaria una nuova
struttura per effettuare query.
Db4o supporta diversi meccanismi per effettuare le query. Per ognuno di essi si
ha come risultato un oggetto di tipo ObjectSet, il quale è una API che ha le stesse
26
Gli indici rendono altamente preformanti le query, aumentando però i tempi di
latenza di modifiche ed inserimenti. Per queste ragioni è sconsigliabile il loro uso
per database di piccole dimensioni, in quanto le perdite in tempo potrebbero essere
superiori ai benefici indotti.
27
Ogni volta che si desidera salvare un oggetto con un indice, Db4o ha bisogno di
aggiungere ulteriori dati. Gli sviluppatori indicano che è consigliabile l’utilizzo degli
indici in schemi di oltre 10.000 oggetti.
28
A confermare l’importanza che questo linguaggio ha avuto nei sistemi relazionali
la dicitura SQL è spesso inserita all’interno del nome di un RDBMS, ad esempio
MySQL, PostGreSQL, SQLite.
68
Capitolo 2
caratteristiche di un iteratore. Utilizzando metodi come HasNext e Next è possibbile
scorrere, vedere se esiste un successivo elemento e spostare di conseguenza l’oggetto
ObjectSet al successivo elemento.
I modi con cui è possibbile effettuare le query vanno sotto il nome di NQ (Native
Query), QBE (Query By Example) e SODA Queries. Da precisare che è possibile
effettuare ricerche sia su tipi semplici di dati come interi, che su Array, stringhe o
addirittura oggetti.
Il compito di scegliere la tipologia di query da utilizzare nell’applicazione è lasciata allo sviluppatore. In alcune circostanze si preferisce avere una soluzione performante. In casi come questi vengono utilizzate le SODA. In altri casi è possibile
richiedere una soluzione confortevole. In questi casi si utilizzano le NQ. Infine ci
citano le QBE, ideali per esercitarsi, ma limitate nelle sue funzionalità.
Per reperire i dati di due oggetti affetti una relazione è sufficiente effettuare solo
una ricerca verso il nodo radice (oggetto principale). In un sistema relazionale invece
è necessario effettuare due query separate oppure un Join. Si può facilmente intuire
che con db4o si possono eliminare molte righe di codice e quindi possibili errori.
Query By Example
Il primo metodo messo a disposizione per effettuare le query sono le Query By
Example. Esse sono sono utili per ricercare oggetti ma hanno funzionalità molto
ristrette.
Il modo più semplice per usare le QBE è passare un oggetto “tipo” (utilizzando
metodi setxxx()) al metodo queryByExample(). Il motore di db4o seleziona tutte le
corrispondenze con l’oggetto creato e ritorna solo quelle trovate.
Le limitazioni delle Query-By-Example riguardano l’impossibilità di effettuare
query contenenti espressioni logiche, oltre alla necessità di dover creare un oggetto
costruttore. Esse non possono inoltre contenere valori di default come 0 nel caso di
numeri, stringhe vuote o valori NULL.
Nel caso in cui si devono effettuare selezioni che vanno al di la delle possibilità
delle QBE, si utilizzano le Native Query e le SODA Query, illustrate nei prossimi
paragrafi.
69
Capitolo 2
Native Query
Le native-query utilizzano la semantica del proprio linguaggio di programmazione
29
, quindi è un ottimo strumento per effettuare ricerche all’interno del database.
Le native-query sono state introdotte in Db4o 5.0 e con esse è possibile utilizzare i
metodi getxxx() delle specifiche classi. In questo modo si possono effettuare ricerche
usando comparatori, range di valori, operatori logici e di uguaglianza (allo stesso
modo è possibile utilizzare operatori composti dalla somma di più operatori logici).
Nel caso in cui il risultato dell’espressione è true viene ritornato l’oggetto specifico.
Ultimo appunto sta nel fatto che internamente db4o cerca di analizzare le native
query per convertirle in SODA, introducendo tempi di latenza superiori.
SODA Query
Le ultime API utilizzate per effettuare le query sono denominate SODA
30
.
Questa metodologia è adatta per la generazione dinamica di selezioni e per la
gestione di query molto performanti, basate sulla nozione di query-graph. Un querygraph lascia allo sviluppatore il compito di scegliere quali oggetti cercare attraverso
il metodo constrain (il quale aggiunge un nodo alla query), ed usare chiamate al
metodo descend per applicare specifici campi di ricerca. Nella struttura a grafo
i nodi rappresentano le classi e gli archi rappresentano le relazioni tra i nodi. È
possibile inserire costanti per ogni nodo per scegliere quali oggetti sono candidati ad
essere inclusi nel risultato della ricerca.
Con le SODA è possibile inserire metodi di comparazione ed operatori logici.
Esistono inoltre comparatori speciali per le stringhe come start-with, ends-with e
like. Come nei dababase relazionali è possibile utilizzare metodi per l’ordinamento
ed elementi riguardanti le QBE (in questo caso si inserisce un oggetto “tipo” (QBE)
e si effettua la query attraverso specifici metodi (SODA)).
29
L’idea di far integrare le query con il proprio linguaggio di programmazione
ha avuto molto successo. Ad esempio la microsoft nel 2006 ha progettato LINQ
(Language Integrated Queries) in una versione del database DLINQ.
30
Le SODA sono state inizialmente create come progetto open-source da Carl
Rosemerger ma con le nuove versioni, questo strumento è completamente integrato
all’interno di Db4o.
70
Capitolo 2
Figura 2.4: Semplice struttura a grafo, per una query SODA, in cui si desidera
selezionare un Oggetto Persona, con attributo nome, uguale a Lincoln.
Spesso le SODA sono difficili da capire ed interpretare, quindi molti programmatori scelgono di utilizzare le Native Query (attraverso strutture del proprio linguaggio
di programmazione).
2.4.4
Transizioni
Tutte le operazioni effettuate in db4o sono caratterizzate da una transizione. Esiste sempre una transizione che si sta eseguendo caratterizzata dall’istanza della
classe Object Container. Ogni transizione agisce implicitamente, ma il progettista
è in grado di decidere quando effettuare una commit
31
per rendere persistenti le
modifiche.
Per effettuare una transizione di commit bisogna invocare l’omonimo metodo
commit() all’oggetto contenitore. Se si incorre in un qualsiasi tipo di problema
(crash, distacco dell’energia elettrica) durate l’operazione, questa viene interrotta e
lo stato del database torna automaticamente all’istante prima della chiamata.
Allo stesso modo della commit, si può utilizzare anche il metodo di rollback.
Utilizzando questo metodo è necessario effettuare una chiamata a refresh() per essere
sicuri che gli oggetti in memoria siano gli stessi contenuti nel database.
31
Oltre ad essere decisa dall’utente la commit è effettuata in modo trasparente
alla chiusura dell’object cotainer.
71
Capitolo 2
Nella modalità client/server offerta dagli sviluppatori di Db4o ci possono essere
più transizioni concorrenti nello stesso momento. In questa situazione si rende necessario l’uso del concetto di isolazione. Con transizioni concorrenti c’è la possibilità
che un utente provi a leggere o modificare gli oggetti del database durante il ciclo di
vita di un’altra che sta operando sugli stessi oggetti. La strategia risolutiva va sotto
il nome di livello di isolazione, tipica dei concetti introdotti nelle proprietà ACID.
Con questa caratteristica il database implementa il livello di isolazione, bloccando
gli oggetti in questione.
2.4.5
Proprietà ACID
Il concetto di ACID 32 è stato una delle prime proprietà riconosciute nella teoria dei
database. Dall’acronimo si intuisce che un database deve rispettare quattro principi.
Atomicità: i dati vengono effettivamente salvati sulla base dati solo se una transizione avviene con successo. In caso contrario lo stato del database non può
cambiare.
Consistenza: un database si trova sempre in uno stato consistente. Ogni transizione lo porta da uno stato consistente ad un’altro.
Isolazione: non è consentita la modifica di dati se una transizione non è completata.
Ad esempio avendo due client che accedono al database operando in maniera
concorrente, si opera in due diverse transizioni completamente isolate tra loro.
I gradi di isolazione vengono divisi in quattro livelli:
Grado zero: una transizione non può modificare i dati utilizzati da un’altra
transizione.
Grado uno: composto dal grado zero, ma non è possibile effettuare una
commit di modifica fino alla fine della transizione.
Grado due: composta dal primo grado, ma non è possibile leggere dati di
altre transizioni.
32
ACID: Atomicity-Consistency-Isolation-Durability
72
Capitolo 2
Grado tre: grado due, ma non è possibile leggere dati di un’altra transizione
prima che è stata effettuata una commit.
Durabilità: concetto base nella teoria dei database, il quale afferma che i dati
devono essere persistenti al suo interno.
2.4.6
Relazioni inverse
Nella realizzazione di un progetto basato su una base di dati ad oggetti, si possono
introdurre le relazioni inverse. Per capire bene il concetto, si propone un esempio
basato sulle normali tecniche di programmazione. Supponiamo di avere una classe
Persona che implementa una seconda classe Indirizzo. Come si può risalire alla Persona che abita nel particolare Indirizzo? In effetti, usando le tecniche di programmazione ad oggetti, questo non è possibile. Per effettuare la navigazione in entrambe
le parti bisogna inserire un nuovo attributo nella classe Indirizzo, aggiungendo cosı̀
un OID verso la classe Persona.
Questa corrispondenza circolare può essere utile nello sviluppo di applicazioni
che utilizzano database ad oggetti, ma molte volte, non è altamente performante,
quindi è lasciato allo sviluppatore l’incarico di scegliere se e come inserire le relazioni
inverse.
2.4.7
Tipologie di relazioni ed Ereditarietà
In ambito applicativo, vengono spesso messi in gioco oggetti composti, creando una
struttura molto complessa da dover progettare. I vari tipi di relazioni si dividono
in: relazioni uno-a-uno, uno-a-molti e molti-a-molti.
Sicuramente la tipologia di relazione più semplice da implementare è quella unoa-uno. In questo caso è necessario dichiarare l’oggetto secondario all’interno di quello
principale.
Le relazioni uno-a-molti sono realizzate mediante l’inclusione di un ArrayList
all’interno di una classe madre. Anche gli ArrayList sono trattati come oggetti; la
lista ed ogni elemento al suo interno hanno un proprio OID.
Nei modelli ad oggetti le relazioni molti-a-molti vengono gestite in maniera analoga al caso precedente con l’uso di ArrayList. In casi come questi potrebbe essere nec73
Capitolo 2
Figura 2.5: Esempio di relazione uno-a-molti, per le classi Impiegato e Manager,
mediante un ArrayList.
74
Capitolo 2
essaria l’inclusione delle relazioni inverse, ma questa scelta resta sempre a discapito
dello sviluppatore.
Un’altra soluzione può essere applicata nel caso in cui le relazioni molti-a-molti
debbano necessariamente includere metodi o attributi. In questi casi, in analogia
con il modello relazionale, si crea una nuova classe intermedia, contenente metodi
ed attributi che si desidera implementare.
Un ultima nota è da dedicare all’ereditarietà 33 , la quale è un concetto chiave nella
programmazione ad oggetti ed è difficile da implementare in un modello relazionale.
Con db4o questa caratteristica è molto semplice da implementare, attraverso la
chiave extends. Ancora una volta si fa riferimento alle OID, per tener traccia di
oggetti ereditati o implementati.
2.4.8
Reference cache
Per archiviare gli oggetti db4o fa uso una una reference-chace. Si tratta di una
tabella che mappa gli oggetti all’interno della memoria mediante l’identificativo
interno (id). Quando si vuole cercare un oggetto, il sistema guarda prima nella
reference-cache, se l’oggetto non è presente viene caricato nel disco.
Di default si fa uso di questa cache, ma questa funzione può essere anche disabilitata. È possibile inoltre rimuovere oggetti da essa manualmente, poichè in
automatico vengono eliminati solo i riferimenti nulli.
2.4.9
Equivalence ed Equality
Nella terminologia utilizzata negli oggetti ci sono due diversi concetti che prendono
il nome di equivalenza ed uguaglianza.
Equivalenza: due oggetti che hanno la stessa OID sono considerati equivalenti.
33
L’ereditarietà è uno dei concetti fondamentali nel paradigma di programmazione
ad oggetti. Essa consiste in un legame che il linguaggio di programmazione, o il
programmatore stesso, stabilisce tra due classi. Nello specifico si ha una classe (classe
figlia o derivata) che estende metodi ed attributi di una classe padre (superclasse).
75
Capitolo 2
Figura 2.6: Esempio di relazione molti-a-molti, per le classi Impiegato e Progetto,
mediante ArrayList e relazioni inverse.
76
Capitolo 2
Figura 2.7: Interazione tra la memoria ed i dati persistenti, mediante cache.
Uguaglianza: se due oggetti hanno lo stesso stato vengono considerati uguali. Ad
esempio se si effettua una query su oggetti di tipo persona si possono avere
oggetti non equivalenti ma che si riferiscono allo stesso oggetto.
2.4.10
Concetto di identità e trasparent persistence
Db4o usa il concetto di identità per identificare gli oggetti, in modo che, se si carica
un oggetto in maniera e tempo diverso viene restituito sempre lo stesso. In maniera
molto semplice, lo stesso oggetto nel database sarà sempre rappresentato dallo stesso
oggetto in memoria. Per implementare questo meccanismo, ogni Object Container
cerca di fare un mapping tra gli oggetti in memoria e quelli salvati. Quando si
caricano gli stessi oggetti attraverso diversi Object Container, essi avranno una
diversa entità in memoria. Solo nel caso in cui si utilizza lo stesso Object Container
gli oggetti vengono rappresentati dalla medesima entità.
Quando si modifica un oggetto vengono salvati solo i cambiamenti avvenuti nello
stesso, modificando gli oggetti di livello inferiore, fino ad una certa profondità (impostata per default questa ad uno). Con questa caratteristica solo i cambiamenti
77
Capitolo 2
avvenuti nello specifico oggetto sono salvati, mentre le modifiche agli oggetti di livello inferiore sono scartate. Per ovviare al problema è possibile scrivere ogni oggetto
individualmente (soluzione utilizzabile per sistemi semplici, ma improponibile per
modelli complessi), oppure usare il concetto di trasparent-presistence.
Mediante l’uso della trasparent persistence (TP) è possibile registrare un oggetto mediante l’uso del metodo store() e lasciare al database il compito di gestire
modifiche future. Questa tecnica porta diversi benefici:
• il codice non dipende dal grado di attivazione
• solo gli oggetti di cui ci interessa visionare i campi sono caricati in memoria
• nessuna modifica è persa.
Per supportare la TP gli oggetti hanno bisogno di implementare l’interfaccia
Activable. Per utilizzare la precedente ipotesi bisogna inserire un attributo di tipo
Activator e due metodi dell’interfaccia Activable. Il primo di essi è in grado di legare
un Activator allo specifico oggetto, mentre il secondo è chiamato prima di ogni
operazione di read o write (nello specifico si deve inserire una chiamate all’interno
dei metodi setxxx() e getxxx()). La precedente metodologia è molto ripetitiva e
sottoposta ad errori; può essere del tutto automatizzata creando build-script oppure
utilizzando file per la configurazione.
Anche per eliminare un oggetto valgono gli stessi ragionamenti fatti per le modifiche. Utilizzando il metodo delete() solo l’oggetto passato al metodo viene eliminato.
In casi come questo gli elementi che rimangono in vita hanno riferimento pari a null.
2.4.11
Concetto di attivazione
Il concetto di attivazione, insieme al concetto di persistenza, è caratteristica fondamentale di db4o e dei ODBMS in generale. Ma perchè si rende necessario l’uso
dell’attivazione? Dimostriamolo con un esempio.
Nella figura sottostante ci troviamo di fronte ad una struttura ad albero tipica
di Db4o. Esiste un solo nodo di root e molti sotto nodi. Nel momento in cui si
recupera l’oggetto root da una query, tutti i sotto oggetti devono essere creati in
78
Capitolo 2
Figura 2.8:
Esempio di recupero di un insieme di elementi, senza l’utilizzo
dell’attivazione.
memoria comportando un grande spreco di risorse se le strutture ad albero sono
molto grandi.
Fortunatamente db4o non segue questa strategia. Quando si recuperano oggetti
da una query, non si recupera tutta la struttura ma solo la parte del grafo d’interesse.
Questo avviene grazie al concetto di attivazione, il quale si verifica quando:
• Si iterano i risultati di una query.
• Gli oggetti sono attivati esplicitamente con i metodi di attivazione nell’Object
Container.
• I membri di una collezione sono attivati automaticamente quando la collezione
è attivata.
Questa funzione è stata impostata di default dagli sviluppatori ed stato scelto di
attivare i nodi fino ad una profondità pari a quattro.
L’attivazione può essere esplicita attraverso l’uso del metodo activate(Object , int
), dove il primo parametro è l’oggetto di riferimento, mentre il secondo è un intero
che specifica il livello di attivazione voluto. Il valore minimo dell’intero è può essere
79
Capitolo 2
Figura 2.9: Esempio di recupero di un insieme di elementi, con l’uso dell’attivazione.
impostato a 1, il quale ritorna solo oggetti a livello più alto con eventuali attributi
primitivi interni alla classe.
Come seconda opportunità è possibile disattivare oggetti precedentemente attivati. In questo caso si utilizza il metodo deactivate(object.get(), int), dove il secondo parametro ha lo stesso significato del caso precedente. Se lo si vuole è possibile anche disattivare l’attivazione settando il parametro numero due del metodo
activate(Object, int) a NULL o default. In questo caso verrà caricato tutto il grafo.
Per concludere è possibile utilizzare il concetto di attivazione in modo trasparente. Questa modalità è configurabile all’interno del database, elimina il problema
dei livelli di attivazione e rende questa proprietà trasparente agli sviluppatori.
2.4.12
Configurazione
Db4o offre molte interfacce per gestire le configurazioni inserite nel package com.db4o.
config, nelle quali è possibile controllare le operazioni di Db4o ed il suo ObjectContainer.
Tutte le configurazioni inserite vengono applicate a partire dal successivo ObjectContainer creato o alla successiva connessione ad un server Db4o e possono essere
applicate a molti oggetti ocntenitori.
80
Capitolo 2
Figura 2.10: Esempio di configurazione, utilizzata per diversi ObjectContainer.
Per inserire le configurazioni è prima necessario creare un oggetto di tipo Configuration nel seguente modo:
Configuration conf = Db4o.Configure()
successivamente è possibile effettuare chiamate ad uno dei seguenti metodi, dove si è
supposto l’esistenza di una classe Persona, cui applicare determinate configurazioni:
conf.ObjectClass(typeof(Person)).ObjectField(name).Indexed(true): che
indica la volontà di creare un indice all’interno della classe Persona sull’attributo “name”.
conf.CbjectClass(typeof(Person)).CascadeOnDelete(): utilizzato per le eliminazioni in cascata degli oggetti a partire da quello principale. L’uso di questa
opzione può essere molto pericolosa nel caso in cui esistono riferimenti multipli
all’interno del database.
conf.CbjectClass(typeof(Person)).CascadeOnUpdate(): come al caso precedente, ma in questo caso si agisce sugli update delle classi.
conf.GenerateUUIDs(Int32.MaxValue): indica la volontà di generare UUID.
Questa configurazione implica l’uso di long object ID, i quali sono unici indipendentemente dalla macchina che li genera. Questi ID fanno diminuire le
prestazioni del database e sono utilizzati nel DRS (si rimanda allo specifico
paragrafo per ulteriori approfondimenti).
81
Capitolo 2
conf.GenerateVersionNumbers(Int32.MaxValue): in questo caso si salva un
numero di versione per ogni oggetto. Utilizzando il parametro -1 si di disabilita
questa opzione, con il valore Int32.MaxValue tutte le classi hanno un numero
di versione, mentre usando 1 si setta il numero di versione solo per le classi
indicate dallo sviluppatore.
conf.AutomaticShutDown(false): questa configurazione impostata per default
a true, indica la possibilità di chiusura automatica dell’ObjectContainer se il
JDK termina o non ha riferimenti al garbadge collector. Inserendo un valore
pari a false si forza lo sviluppatore ad inserire il metodo close() per chiudere
il container.
conf.LockDatabaseFile(false): metodo impostato a true per default per la gestione degli accessi concorrenti al database. Impostando il parametro a false
nessuna operazione di look viene effettuata.
conf.SingleThreadedClient(true): configurazione utilizzata nella modalità clientserver. Impostando il parametro a false la gestione dei client avviene su
un singolo thread, mentre un parametro impostato a true abilita i client alla modalità multithreaded, la quale è molto performante per lo scambio di
messaggi asincroni ma utilizza più risorse.
conf.WeakReferences(false): metodo che indica l’utilizzo di una weak reference
(introdotta i precedenza). Questo approccio consuma molta memoria poichè
il garbadge collector non può pulire oggetti inutilizzati; è necessario effettuare
una chiamata ad ObjectContainer.purge(object) per rimuovere tutti gli oggetti
inutilizzati.
I successivi esempi di configurazione influenzano il file del database. È possibile
con essi cambiare la struttura, il contenuto del database e come nei casi precedenti
è necessaria la creazione di un oggetto di tipo Configuration.
conf.BlockSize(8): questo metodo imposta la grandezza dei blocchi del file relativo
al database. Il valore può essere un numero da 1 a 127 ma deve sempre essere
multiplo di 8. Il valore di default è 1 e con questo valore il database può
82
Capitolo 2
raggiungere i 2GB. Gli sviluppatori raccomandano un valore pari ad 8 in modo
da raggiungere i 16GB di grandezza.
conf.Encrypt(true): db4o può effettuare due algoritmi di crittografia. Il metodo
più semplice imposta questo metodo con il parametro true e successivamente si
inserisce una password. Questo sistema introduce un livello di sicurezza molto
basso, utilizzabile se non ci sono particolari vincoli a riguardo.
conf.Io(new XteaEncryptionFileAdapter(password)): questa configurazione
permette agli sviluppatori di configurare il proprio Adapter I/O; è possibile
crearlo o utilizzare uno fornito da Db4o (contenuti nel package com.db4o.io).
conf.Unicode(false): db4o è in grado di supportare gli Unicode come formato di
memorizzazione delle stringhe.
Tra i metodi di configurazione ci sono anche opzioni applicabili per un singolo
ObjectContainer o ObjectServer. In questo modo è possibile effettuare modifiche
della configurazione per particolari ObjectContainer o ObjectServer, mantenendo
inalterate le configurazioni globali.
2.4.13
Client-server mode
Un sistema client-server consiste in genere in un numero non definito di client che
interagiscono con un unico server. I server forniscono servizi attraverso deamon
34
che ascoltano ed iniziano delle connessioni con i client.
Come già affermato, le API di db4o includono la funzione opzionale denominata
client-server mode. Questa modalità richiede la creazione di un oggetto ObjectSercer,
cui passare il nome del file da aprire e la porta da utilizzare.
34
Un demone (tradotto erroneamente dall’inglese, daemon) è un programma che
agisce in background, cioè senza che sia sotto il controllo diretto dell’utente. In
generale hanno la funzione di rispondere a determinate richieste che siano di rete,
hardware, etc. La loro caratteristica fondamentale a differenza dei normali programmi è che i demoni sono normalmente in esecuzione per tutta una sessione di lavoro.
Il motivo è che devono sempre essere in ascolto per soddisfare eventuali richieste
provenienti dall’utente o dall’esterno.
83
Capitolo 2
Figura 2.11: Esempio di di comunicazione tra un server e diversi client.
Db4o supporta tre diversi tipi di interazioni tra client e server. La prima modalità
è definita networking mode e viene utilizzata quando si lavora con un database remoto. In questo caso i client aprono una connessione TCP/IP per eseguire istruzioni
di inserimento, modifica, eliminazione o query da e verso il database.
La seconda modalità è definita embedded mode e non coinvolge sistemi distribuiti.
Anche in questo caso il client ed il server sono attori ben distinti, ma al contrario
del caso precedente essi si trovano nella stessa virtual machine. Questa seconda
modalità non è molto diversa dalla precedente, infatti è necessario passare l’intero
0 al metodo che apre il server (il quale indica che il server si trova in locale).
L’ultima modalità è utilizzabile per comunicazioni out-of-band verso il server. In
questo caso le informazioni passate non appartengono al protocollo db4o e non consistono in oggetti. È possibile inviare messaggi verso il server tipo: do a defragment,
stop yourself, perform a savecopy.
Mediante l’utilizzo di una delle tre modalità client/server è stato necessario introdurre un sistema di accesso controllato ai dati. È necessario infatti specificare
username e password per effettuare il login con il server.
Anche nelle modalità viste in questo paragrafo si utilizza una cache per l’accesso
ai dati persistenti. In questo caso ogni client ha una cache di riferimento, la quale
aiuta ad avere buoni tempi di risposta. La presenza della cache comporta tanti
problemi quando diversi client lavorano con gli stessi oggetti, ma questi posso essere
saltati utilizzando diverse strategie illustrate di seguito.
• Se gli utenti non lavorano con gli stessi oggetti nello stesso momento non
84
Capitolo 2
Figura 2.12: Esempio di una struttura client-server, che racchiude tutti i casi
prededentemente trattati.
sussiste il problema.
• Si può fare il refresh ogni volta che un client effettua una operazione di modifica
o inserimento, il quale comporta un notevole scambio di dati.
• Minimizzare la cache dei vecchi oggetti mediante l’uso di sessioni separate nel
client. In questo modo si crea un nuovo container ed una cache pulita.
Comunicazione SSL
Per default db4o non effettua una comunicazione criptata, però è possibile la comunicazione SSL
35
.
Per effettuare una comunicazione di questo tipo si deve semplicemente implementare il SSLSupport
36
.
35
Il Secure Sockets Layer(SSL) è uno dei protocolli crittografici che permettono
una comunicazione sicura e una integrità dei dati su reti TCP/IP, come ad esempio
Internet. SSL cifra la comunicazione dalla sorgente alla destinazione al di sopra del
livello di trasporto.
36
Classe utilizzata per la creazione di una connessione SSL in ambito Db4o. Il
costruttore di questa classe non necessita di nessun parametro.
85
Capitolo 2
2.4.14
DRS. Data Replication System
Il concetto di Replication [13] è definito da wikipedia.com nel seguente modo:
the provision of redundant resources (software or hardware components) to improve
reliability and fault-tolerance.
Nel nostro caso si parla di risorse software e più precisamente dei dati salvati nel
database.
Per effettuare la sincronizzazione si fa uso di un ObjectContainer e un peer
37
,
rappresentato da un identificatore unico per tenere traccia dell’origine degli oggetti.
Grazie alla natura dell’identificatore, non possono esservi due peer con lo stesso
valore.
È possibile scegliere di replicare tutti i dati (oppure alcuni) di un peer verso un
altro
38
. Esso è spesso unidirezionale ma è possibile renderlo bidirezionale.
Il DRS è stato disegnato per per avere un altro grado di integrazione tra dispositivi mobili e applicazioni web (o destokop). Casi d’uso sono ad esempio l’integrazione di apps commerciali sincronizzate con un database sito su di un server.
Questo concetto potrebbe anche essere valido quando è impossibile aggiornare i dati
di un database remoto ovunque; attraverso il DRS è possibile farlo in un secondo
momento.
Per utilizzare questo sistema di replicazione è necessario assegnare un UUID
39
ad ogni oggetto e un numero di versione. Queste ipotesi non sono verificate da db4o
come default, quindi bisogna configurarle esplicitamente.
37
Con il termine peer si intende dire che ogni nodo può fungere sia da cliente che
da servente. Questa terminologia è molto utilizzata in sistemi P2P (peer to peer),
dove esisto un numero indefinito di nodi con le precedenti caratteristiche.
38
Operando su dispositivi mobili le risorse sono limitate protrerrebbe essere
sbagliato replicare l’intero database. In questi casi è possibile replicare solo alcune
parti scelte mediante una semplice condizione o con una query più complessa.
39
UUID: Unique universal identifiers. Il formato degli UUID è stato standatdizzato dalla Open Software Foundation. Un UUID è un numero a 128 bit, il quale
contiene una firma del database generata da un algoritmo che contiene il nome dell’host, l’indirizzo, il timestamp corrente e due valori randoom di tipo long. Gli
UUID sino salvati come istanze di classe Db4oUUID, i quali contengono una parte
per la firma ed una parte contenente l’identificatore all’oggetto. Oltre i precedenti
è necessario l’utilizzo di un numero di versione del database.
86
Capitolo 2
Figura 2.13: Esempio di Data Replication System, in un possibile contesto reale.
87
Capitolo 2
Il DRS può essere utilizzato attraverso la sincronizazione tra db4o-to-db4o, db4oto-Hibernate/RDBMS, db4o-to-VOD ed Hibernate/RDBMS-to-Hibernate/RDBMS.
Esso non è implementato all’interno di Db4o, quindi è necessario includere delle
specifiche librerie rilasciate dagli stessi sviluppatori Versant.
2.4.15
Db4o ed SQL (SQLite)
Nella letteratura spesso si associa al termine di database alla struttura tipica dei
RDBMS formata da tabelle. Questo è dovuto al fatto che il 90% del mercato è
governato da modelli relazionali. Negli ultimi anni però, con l’aumentare del successo
dei linguaggi di programmazione ad oggetti, è stato necessario fornire un’alternativa
ai database relazionali.
Db4o ed SQLite sono ambedue embedded database utilizzabili durante l’esecuzione di un’applicazione. Essi rimuovono il layer associato ad una configurazione
client-server, però Db4o supporta la modalità client-server e la crittografia (che può
essere utilizzata in tutte le situazioni).
Con database Versant ogni azione è associata ad una transizione. Una transizione
parte quando il database viene aperto e termina alla sua chiusura. Le transizioni
sono implicite quando viene effettuata la chiamata ai metodi commit() e close(). Con
SQLite invece, la funzione di autocommit è utilizzata di default ed una transizione
parte utilizzando comandi SQL.
Mediante l’utilizzo di database relazionali si devono scrivere righe di codice per
la creazione del database, delle tabelle e dei suoi campi. In Db4o questo non è
richiesto; non sono richiesti codici per la creazione e mantenimento del database.
Aprire o chiudere la connessione è molto semplice in entrambi i casi. Il vero
vantaggio di db4o sta nell’inserimento dei dati nel database. Usando un RDBMS
bisogna inserire i vari elementi nelle diverse tabelle, mentre con un ODBMS bisogna
effettuare una sola chiamata al metodo di store contenente l’oggetto di riferimento,
quelli in relazione e collezioni di essi.
Altra considerazione importante avviene quando si cerca di effettuare delle query.
Naturalmente utilizzando un database relazionale si è costretti a ricostruire degli
oggetti a partire dal risultato della query. Questo implica un notevole lavoro per lo
sviluppatore che deve necessariamente creare dei metodi adatti a farlo.
88
Capitolo 2
Db4o supporta metodi per effettuare il backup del database, mentre non è possibile in SQLite. Siccome il database è contenuto in un unico file, in questo caso, per
effettuare un backup, si deve necessariamente copiare a mano il file in questione.
2.4.16
Performance
Le performance sono un fattore chiave per i database; anche per Db4o non ci sono
eccezioni. È necessario effettuare dei test con varie combinazioni hardware ed applicazioni. Tutto questo è già stato implementato in un benchmarks 40 scritto in Java,
denominato PolePosition [14].
Attraverso questo progetto open-source si evincono i pro ed i contro dei database
ad oggetti ed in particolare su Db4o. Questo benchmarks racchiude una serie di test
e risultati in una grande varietà di database che vanno dai RDBMS ai ODBMS.
Nella tabella sottostante si mostrano i risultati che illustrano i casi in cui Db4o
opera molto bene. I test possono essere raccolti nei seguenti casi:
• Scrivere un volume lineare: semplice esempio in cui scrivono 10,000 oggetti di
un solo tipo, senza dipendenze.
• Leggere, scrivere ed eliminare un albero: in questo test si eseguono delle
semplici query, all’interno di un grafo composto da otto livelli e da semplici
oggetti.
• Query ed ereditarietà: in questo test si effettuano delle normali operazioni
in una serie di grafi a 5 livelli; ogni albero contiene cinque oggetti, dove il
successivo deriva da quello precedente.
• Query indicizzata: in questo caso si mostra quanto può essere veloce una query
verso un singolo oggetto, usando un indice su di un campo intero.
• Recuperare un oggetto dall’ID: in contrasto con quello che avviene nel caso
precedente, si effettua una query attraverso l’ID.
40
Con il termine benchmark si può intendere un insieme di test software volti
a fornire una misura delle prestazioni di un computer o la determinazione della
capacità di un software di svolgere più o meno velocemente un particolare compito
per cui è stato progettato.
89
Capitolo 2
Figura 2.14: Tabella che mostra i casi in cui, Db4o è altamente performante rispetto
agli altri sistemi.
Figura 2.15: Tabella che mostra i casi in cui, i database sono altamente performanti.
Dai risultati ottenuti si verifica ciò che è facile attendersi; nel caso in cui si ha
il bisogno di creare una struttura molto complessa, Db4o è altamente performante
rispetto ad altri DBMS. Contrariamente, se si devono effettuare delle semplici query,
l’SQL e quindi i RDBMS sono più adatti. Questa caratteristica è rappresentata nella
seguente tabella, la quale riassume i seguenti casi:
• Query per le string: query attraverso un valore di tipo string, su 1,000 oggetti.
• Delete: eliminazione di 1,000 oggetti a partire dall’ID.
I precedenti test utilizzano db4o nella modalità locale e si sono utilizzate le SODA
query. Riscrivendo il codice utilizzando le native queries si riscontra un aumento
della latenza del 10%. Questo dimostra anche che le SODA queries hanno maggiori
performance.
90
Capitolo 2
2.5
Perst
Come Db4o, Perst
41
è un database ad oggetti embedded, open-source a doppia
licenza 42 . Esso è disponibile per due linguaggi di programmazione: Java e C# (per
applicazioni .NET) e fa uso degli OID per creare riferimenti tra gli oggetti.
La distribuzione è anche disponibile in una versione Lite per applicazioni Java
che girano su telefoni cellulari, PDAs ed altri dispositivi basati su Java ME. Questa
è una versione semplificata nella quale sono state eliminate diverse funzioni basilari
come l’accesso da parte di più client o il look di file. Questa seconda versione occupa
circa il 30% di spazio in meno rispetto quella completa.
2.5.1
Salvataggio degli oggetti
Con Perst è possibile il salvataggio degli oggetti utilizzando un istanza della classe
Storage, ottenuta a sua volta con i metodi della classe StorageFactory.
Per ottenere i riferimenti di tutti gli elementi creati si utilizza un oggetto chiamato root. Questo è il solo elemento persistente sul quale si accede in modo speciale,
mentre gli accessi agli altri avvengono attraversando gli oggetti. Il sistema può avere
solo un oggetto root e si utilizzano i metodi Storage.getRoot o Storage.setRoot per
ottenere il riferimento o creare una istanza.
L’uso della persistenza avviene con la classe Persistent. Per salvare un nuovo
elemento è necessario recuperare l’oggetto root ed in un secondo momento possono
essere salvati nuovi elementi nel database attraverso il metodo store.
Per il salvataggio degli elementi è possibile anche utilizzare una tecnica che
prende il nome di persistenza per raggiungibilità. Per diventare persistente un oggetto deve essere referenziato da qualche altro elemento persistente. L’unica eccezione
41
Perst è stato creato da Konstantin Knizhnik nel 2003. Nel 2006 Perst è stato
trasferito a McObject, industria for-profit, sita a Washinton (Stati Uniti). Prima
della versione 4.0 le API seguivano JDK 1.4, mentre a partire da questa si utilizza
JDK 1.5 e superiore.
42
Gli utenti possono reperire e modificare Perst sotto i termini della GNU (General
Public Licence) come pubblicato dalla Free Software Foundation. Per singoli o
organizzazioni che non possono o non vogliono rispettare i termini della GPL è
disponibile una licenza commerciale.
91
Capitolo 2
è data all’oggetto principale che diventa persistente quando viene registrato come
radice di archiviazione.
Tutti gli oggetti all’interno della base dati possono essere esplicitamente eliminati
utilizzando il metodo deallocate, o implicitamente alla chiamata dal garbage collector
Perst, il quale de-alloca tutti gli oggetti persistenti che non sono raggiungibili dal
root.
2.5.2
Relazioni tra oggetti
Come per gli altri DBMS, anche in Perst si possono creare relazioni tra gli oggetti
nella base di dati. La tipologia più semplice da implementare è sicuramente quella
uno-a-uno. In questa situazione basta inserire il riferimento di una classe A in una
seconda classe B.
Il discorso è leggermente più complesso in relazioni uno-a-molti e molti-a-molti.
In queste situazioni bisogna inserire collezioni di elementi all’interno degli oggetti,
le quali possono essere racchiuse in:
Standard Java Array.
Standard Java Collection. Perst salva queste collezioni all’interno di oggetti persistenti per rendere possibile il loro utilizzo.
Perst Link. Analoghe ad un ArrayList Java, ma a differenza di esse, si caricano
gli elementi di un Link solo quando viene effettuata una richiesta su di essi.
Come per gli Array, un Link non è un oggetto separato ma è inserito in quello
contenitore.
Perst Relation. L’unica differenza tra essi e i Link è che essi sono salvati come
oggetti separati nel database.
Perst list. Implementazione di una lista persistente basata su un B-Tree in cui è
possibile l’accesso mediante gli indici.
Perst set. Anche essi sono implementati usando un B-Tree, ma in essi non si può
avere accesso mediante gli indici.
92
Capitolo 2
2.5.3
Attivare gli oggetti
Questo sistema offre due diverse soluzioni per attivare gli oggetti. Esse sono:
Ricorsione Esplicita: si lascia al programmatore il compito di caricare esplicitamente tutti glioggetti.
Ricorsione Implicita: carica in modo ricorsivo tutti gli oggetti di riferimento. In
questa situazione invocando l’oggetto root viene caricata tutta la base dati, il
che è indesiderabile, specialmente lavorando con database di grandi dimensioni.
2.5.4
Cercare Oggetti
Il meccanismo di caricamento degli oggetti in Perst ha dei vincoli imposti per consentire alle query di essere molto preformanti. Gli oggetti vengono salvati in collezioni
implementati come B-Tree; inoltre i programmatori sono costretti ad inserire gli
indici per il salvataggio degli oggetti.
I B-Tree sono costituiti da pagine di grandi dimensioni consententi coppie key,
value, dove key è una chiave del valore e cioè un indice.
Gli indici possono essere di cinque tipologie, le quali sono racchiuse nelle seguenti
righe.
Indici semplici: indice semplice creato utilizzando il metodo createIndex della
classe di Index.
Indici e chiavi: nella maggior parte dei casi, una chiave viene memorizzata in uno
dei campi dell’oggetto. Per effettuare questa operazione viene fornito il metodo
createFieldIndex nel quale si specifica il nome del campo chiave.
In questa tipologia, se la chiave deve essere aggiornata, l’oggetto deve prima
essere cancellato a partire dall’indice e poi reinserito, effettuando l’aggiornamento con il nuovo valore di chiave.
Indici spaziali: l’uso diffuso di dispositivi GPS e la popolarità di servizi come
Google Maps aumenta in modo significativo il numero di applicazioni che lavorano con dati di tipo geografico. Perst consente l’uso di due metodi per la
reazione di indici spaziali.
93
Capitolo 2
Indice composto: il database supporta un indice composto da una chiave di diversi
valori.
Indice multidimensionale: un indice multidimensionale può essere utilizzato nelle
ricerche dove ci sono un numero maggiore di uno di criteri da utilizzare.
Ad esempio, un indice su lastName,firstName consente la ricerca di persone
specificando nome e cognome, solo cognome, ma non utilizzando solo il nome.
2.5.5
Transizioni
Ogni operazione effettuata con Perst avviene all’interno di una transizione, in modo
da salvare e modificare gli oggetti solo al termine di essa. Questo avviene automaticamente portando il database da uno stato consistente ad un altro. La principale
pecca di questo approccio si ha quando di vuole supportare un accesso concorrente
dove diversi utenti vogliono lavorare con gli stessi oggetti nello stesso momento
(questa situazione è comunque risolvibile).
2.5.6
Relational Database Wrappers
Mediante il wrapper si provvede ad una serie di API che rendono simile l’uso del
database ad un modello relazionale. La classe utilizzata per questo approccio, di
nome Database, provvede alle seguenti funzioni:
• Non necessita di un oggetto root.
• Mantiene gli indici creati dal programmatore o dal sistema
• Usa il blocco delle tabelle.
• Provvede ad un linguaggio per effettuare le query denominato JSQL.
JSQL
È possibile considerare JSQL come un parente di SQL in cui non è necessario specificare le clausole SELECT, FROM e WHERE, poichè si lavora con gli oggetti e non
con tuple e tabelle.
94
Capitolo 2
Per effettuare ricerche con JSQL bisogna utilizzare la classe Query e per la sua
esecuzione è necessario definire un iteratore (Iterator ) ed una stringa rappresentante
le condizioni.
Questo nuovo lunguaggio provvede ad un meccanismo per localizzare velocemente gli oggetti attraverso una chiave primaria. In questo caso si ha l’analogia
degli indici utilizzati in un database di tipo relazionale.
2.5.7
Repliation System
Perst supporta un sistema di tipo master-slave in cui può esistere un solo nodo
master su cui aggiornare il database e più nodi di slave che effettuano richieste di
lettura al master.
In questa architettura, le operazioni possono essere effettuate in modo asincrono
senza attendere conferma dalla replica, o sincrono, attendendo conferma dalla replica
prima di effettuare la transazione.
Il sistema fornisce un’altra opzione di replica detta statica quando il numero di
replicanti è fissato al momento dell’apertura del master, o dinamica, in cui nuovi
slave possono collegarsi al nodo master.
2.5.8
Perst vs Db4o
La differenza che viene subito alla mente tra i due sistemi è sicuramente il diverso
grado di trasparenza offerto. Le operazioni in Db4o sono molto intuitive, mentre
con Perst tutte le operazioni e il database derivano da una classe.
Le precedenti affermazioni si trasformano in due note:
• Il codice è più fluido senza l’utilizzo di classi.
• Senza questo livello di trasparenza è più facile incorrere in errori.
In Db4o sono concesse un numero molto grande di opzioni configurabili, varie
tipologie di connessioni client-server ed un sistema di replicazione bidirezionale. Dei
precedenti punti, Perst fa uso di un numero esiguo di configurazioni, gestisce un
sistema di replicazione master-slave e non è in grado di stabilire una connessione
client-server se non con il sistema di replicazione.
95
Capitolo 2
Nota di merito a Perst è dettata dalle sue prestazioni. Come Db4o è altamente
performante su un numero di oggetti molto grande contenuti nella base di dati.
In questa situazione dai test effettuati su benchmark, si evince che Perst effettua
operazioni di inserimento, modifica ed eliminazione in tempi (in alcuni casi secondi)
minori rispetto Db4o.
96
Capitolo
3
Realizzare un’applicazione Android-Db4o
3.1
Introduzione
Per creare applicazioni in Android e Db4o, prima di ogni cosa è necessario scaricare
gli strumenti adatti. Si parla in questo caso dell’SDK e dei pacchetti .jar relativi al
database, entrambi scaricabili nella sezione sviluppatori, all’interno delle rispettive
pagine web ufficiali.
É strettamente consigliabile l’uso dell’IDE Eclipse sul quale viene fornoto l’OME
1
plug-in di Db4o, ed i riferimenti all’SDK per effettuare operazioni come la creazione
di un nuovo AVD utilizzando l’interfaccia grafica.
Avendo effettuato le precedenti operazioni è possibbile creare ogni tipo di applicazione, ed in questo caso una in particolare, alla quale saranno dedicate le prossime
sezioni.
3.2
Il problema
L’applicazione che è stata implementata nella tesi prevede la gestione di un campionario di merci da parte di un venditore e/o rappresentante, il quale ha il com1
OME: Object Manager Enterprise è il plugin messo a disposizione dalla Versant
per la visione degli oggetti contenuti nel database e per effettuare semplici query su
di esso.
97
Capitolo 3
pito di rappresentare prodotti mediante APPUNTAMENTI ed effettuare eventuali
ORDINI da consegnare ai CLIENTI.
L’applicazione prevede l’inserimento di PRODOTTI da parte del venditore, ognuno dei quali è caratterizzato da una categoria (alimenti, elettronica ecc.), da una
o più immagini. Ogni prodotto può essere venduto singolarmente o a pacchi e nel
secondo caso sarà necessario specificare quanti elementi sono presenti all’interno di
ogni singolo pacco.
Ogni venditore è in grado di inserire APPUNTAMENTI che si avranno con i
clienti, i quali sono caratterizzati da un indirizzo.
Un cliente ha un indirizzo di riferimento, contatti (telefono, cellulare, e-mail)
e può avere zero o più ordini (con eventuali SCONTI ), i quali sono composti da
una lista di prodotti scelti (con relativa quantità), una DATA, una modalità di
pagamento, uno stato ed un costo.
3.3
Casi d’uso
In UML, gli Use Case Diagram o diagrammi dei casi d’uso possono considerarsi
come lo strumento di rappresentazione dei requisiti funzionali di un sistema. In
sostanza spiegano in che modo è possibile interagire con il sistema ma non come
queste operazioni avvengano. Ogni diagramma è caratterizzato da diversi elementi
che possono relazionarsi tra loro.
Tra gli elementi principali si citano:
System rappresenta il contenitore degli elementi delle caratteristiche del sistema.
Questi sono rappresentati graficamente mediante un rettangolo vuoto all’interno dei quali vengono rappresentate le entità interne, mentre le entità esterne
(appartenenti al dominio o al contesto del sistema) sono posizionate all’esterno.
Actor un attore è una classe con stereotipo actor rappresentante un ruolo coperto
da un certo insieme di entità che interagiscono con il sistema, quali utenti
umani, sistemi software o dispositivi hardware. Gli attori sono rappresentati
graficamente da un’icona che rappresenta un uomo stilizzato.
98
Capitolo 3
Use Case un caso d’uso è rappresentato graficamente come un’ellisse contenente il
suo nome. Si può intendere questo strumento come una classe di comportamenti correlati agli Attori.
Le associazioni che incorrono nel sistema possono essere dei seguenti tipi:
Actor e use case questo tipo di associazione congiunge gli attori con i casi d’uso
a cui essi partecipano. Ogni attore può essere associato a un qualsiasi numero
di casi d’uso ed essi implicano uno scambio messaggi.
Generalizzazione In questa situazione, un “sottoattore” è in grado di partecipare
a tutti i casi d’uso a cui partecipa il “superattore”, ed eventualmente può
partecipare a use case aggiuntivi.
Inclusione la relazione di inclusione indica che la funzione rappresentata da uno dei
due use case include completamente la seconda. Le inclusioni vengono rappresentate graficamente da una linea tratteggiata con indicazione dello stereotipo
include.
Estensione indica che la funzione rappresentata dallo use case “estendente” può
essere impiegata della funzione “estesa”. Questa situazione è rappresentata da
una linea tratteggiata con indicazione dello stereotipo extend.
Il diagramma dei casi d’uso può essere molto utile per la realizzazione della nostra
applicazione BAgent. Il successio paragrafo è dedicato allo scopo.
3.3.1
Casi d’uso Capionario
I casi d’uso relativi all’applicazione BAgent possono essere raccolti nella figura 3.1.
Nello specifico, esiste un solo Attore che opera nell’applicazione:
Venditore il quale interagisce con BAgent fornendo un insieme di dati riguardanti
ordini, prodotti, appuntamenti e clienti, i quali verranno inseriti all’interno del
database ad oggetti.
Il venditore ha a disposizione anche una serie di opzioni, ad esempio per la
gestione dell’accesso all’applicazione mediante una password.
99
Capitolo 3
ud: Campionario
Gestione Clienti
Gestione Ordini
Gestione Prodotti
Gestione Categorie
Venditore
Gestione Tipologie Prodotti
Figura 3.1: Diagramma dei casi d’uso, dell’applicazione BAgent.
100
Capitolo 3
I casi d’uso invece, sono i seguenti:
Gestione Prodotti raccoglie elementi di inserimento, modifica ed eliminazione dei
prodotti. Ogni prodotto può essere inserito all’interno degli ordini ed ogniuno
di essi è caratterizzato da una propria categoria e da una o più immagini
scattate direttamente dalla fotocamera.
Un venditore può anche effetturare ricerche a seconda della tipologia o del
nome del prodotto.
Gestione Ordini anche in esso sono racchiuse le operazioni di inserimento, modifica ed eliminazione. Un ordine può anche essere caratterizzato da uno sconto
associato e da uno stato (Attesa, Pagato, Bloccato).
Per gli ordini è consentita la ricerca per stato, data di creazione o cliente.
Gestione Appuntamenti al suo interno sono racchiuse le solite operazioni di inserimento, modifica ed eliminazione, inoltre è consentito l’eliminazione degli
appuntamenti passati e la ricerca per data o cliente.
Gestione Clienti incluso in esso vi sono le opersazioni di inserimento, modifica ed
eliminazione, oltre alle ricerche per cognome o codice fiscale. Per ogni cliente è
posibbile effettuare chiamate, inviare SMS e gestire l’importazione dei contatti
da e verso la rubrica del telefono.
Gestione Tipologie da associare ad un prodotto mediante un nome l’iva ed una
lista di attributi specifici.
È possibile creare, modificare o eliminare ogni tipologia.
3.4
Architettura logica
La creazione dei progetti in Android richiede delle specifiche imposte dagli stessi
progettisi. Ogni file deve essere inserito in un determinato folder per evitare la
generazione di errori. Ad esempio, i file XML per il layout vanno sotto la cartella
/res/layout, le risorse nella cartella /res/values, mentre le classi che specificano le
Activity vanno inserite all’interno di opportuni package.
101
Capitolo 3
Questa architettura introduce una netta separazione tra la logica di esecuzione e
la logica di presentazione delle applicazioni. Per questo motivo si è scelto di marcare
questa scelta anche all’interno delle classi Java mediante l’utilizzo del pattern MVC.
3.4.1
MVC ed organizzazione dei package
L’MVC (Model View Controller) è un pattern
2
architetturale molto diffuso nello
sviluppo di interfacce grafiche di sistemi software object-oriented. Il pattern è basato
sulla separazione dei compiti in tre ruoli principali:
• il model, che fornisce i metodi per accedere ai dati dell’applicazione.
• le view, che visualizzano i dati contenuti nel model e si occupano della presentazione dei dati.
• il controller, che riceve i comandi dell’utente, i quali si ripercuotono nel sistema
con la modifica degli altri due componenti.
I dettagli delle interazioni fra le precedenti entità dipendono molto dalle tecnologie usate (linguaggio di programmazione, eventuali librerie ecc.) e dal tipo di
applicazione (applicazione web, desktop ecc.).
Il core dell’applicazione viene implementato dal Model che incapsula lo stato e
definisce i dati e le operazioni che possono essere eseguite su questi. Esso quindi
definisce le regole di interazione con i dati, esponendo alla View ed al Controller le
funzionalità per l’accesso e l’aggiornamento.
La logica di presentazione dei dati viene gestita dalla View. Ciò implica che questa deve fondamentalmente gestire la costruzione dell’interfaccia grafica (GUI) che
rappresenta il mezzo mediante il quale gli utenti interagiranno con il sistema. Ogni
GUI può essere costituita da schermate diverse, le quali svolgono diverse funzioni
all’interno dell’applicazione.
L’ultima generalizzazione è composta dal Control, il quale ha la responsabilità
di trasformare le interazioni dell’utente della View, in azioni eseguite dal Model.
2
Nell’ingegneria del software un pattern può essere definito come una soluzione
progettuale generale ad un problema ricorrente che si presenta in diverse situazioni
durante la progettazione e lo sviluppo del software.
102
Capitolo 3
Queste classi realizzano la mappatura tra input dell’utente e processi eseguiti dal
Model e selezionano le schermate (View ) richieste, quindi si può affermare che il
Controller implementa la logica di controllo dell’applicazione.
BAgent: Architettura in package
Nell’applicazione BAgent è stato introdotto il pattern MVC, il quale implica una
diversa organizzazione in package a seconda del contesto.
I package possono essere racchiusi nei seguenti punti:
com.android.campionario.Controller utilizzato per la gestione delle classi Controller. Le classi incluse in questo folder iniziano con la lettera C.
com.android.campionario.View per la gestione delle View, dove le classi incluse,
iniziano con la lettera V.
com.android.campionario.Entity in questo package sono descritti gli oggetti
specifici del dominio in questione. Ogni classe inizia con la lettera E ed è
in grado di gestire oggetti in memoria centrale, i quali in questo caso sono
anche persistenti, cioè recuperati dal database Db4o.
com.android.campionario.Foundation le classi Foundation conoscono l’implementazione a livello fisico dei dati ed introducono dei metodi per accedere al
database Db4o.
Tutte le sottoclassi ereditano una superclasse Foundation, la quale contiene
metodi generici per l’accesso al database utilizzati da ogni sottoclasse.
Utilizzando un database relazionale ogni Entity si tracuce in una classe Foundation, per via della natura in tabelle del database. Utilizzando Db4o, si può
ricorrere in un approccio meno dispensioso, perchè operazioni di inserimento,
modifica ed eliminazione possono essere effettuate sia sugli oggetti pricipali,
sia in quelli inclusi in essi. Questa affermazione può essere tradotta in un
numero di classi minori rispetto le Entity.
Insieme alle precedenti classi e package si possono essere anche citate le classi
Boundary che realizzano la GUI, cioè l’interfaccia dell’applicazione.
103
Capitolo 3
3.4.2
Pattern Singleton
Il Singleton è un design pattern con lo scopo di garantire che ad ogni classe venga
creata una ed una sola istanza, oltre a fornire un punto di accesso globale ad essa.
Questo pattern è utilizzato per implementare il concetto matematico “singleton” 3 ,
restringendo l’instanziazione delle classi verso un solo oggetto.
L’implementazione più semplice di questo pattern prevede una classe singleton
con un unico costruttore privato, in modo da impedire l’istanziazione diretta. La
classe fornisce inoltre un metodo “getter” statico che memorizza il riferimento in un
attributo privato.
BAgent: Singleton
Il concetto esposto in precedenza può essere esteso in applicazioni che lavorano con
le classi. Nel caso dell’applicazione da creare viene utilizzato sia il linguaggio di
programmazione Java e sia un database ad oggetti. Il database mantiene al suo
interno i rifermimenti tra gli oggetti in memoria e quelli all’interno del database. In
operazioni di modifica o eliminazione, si deve essere certi di operare con il medesimo
oggetto e per farlo è stato necessario introdurre questo pattern nell’applicazione
mediante la classe SingletonPatternBridge espressa nelle seguenti righe di codice.
public c l a s s SingletonParametersBridge
{
private s t a t i c SingletonParametersBridge instance = null ;
p r i v a t e HashMap<S t r i n g , Object> map ;
public s t a t i c SingletonParametersBridge getInstance () {
i f ( i n s t a n c e == n u l l )
i n s t a n c e = new S i n g l e t o n P a r a m e t e r s B r i d g e ( ) ;
return instance ;
}
3
In matematica un singoletto (o singleton) è un insieme contenente esattamente
un unico elemento. Ad esempio un insieme è un singoletto se e solo se la sua
cardinalità è 1
104
Capitolo 3
private SingletonParametersBridge () {
map = new HashMap<S t r i n g , Object > ( ) ;
}
p u b l i c b o o l e a n f i n d P a r a m e t e r ( S t r i n g key ){
r e t u r n map . c o n t a i n s K e y ( key ) ;
}
p u b l i c v o i d addParameter ( S t r i n g key , Object v a l u e ) {
map . put ( key , v a l u e ) ;
}
p u b l i c Object getParameter ( S t r i n g key ) {
r e t u r n map . g e t ( key ) ;
}
p u b l i c v o i d removeParameter ( S t r i n g key ) {
map . remove ( key ) ;
}
}
nel quale si è creato un attributo map di tipo HashMap, dove il primo parametro è
una stringa identificativa, mentre il secondo un oggetto.
I metodi findParameter, addParameter e removeParameter, sono utilizzati rispettivamente per cercare una certa chiave per aggiungere un novo oggetto o per la
rimozione di esso.
3.5
Modello di dominio
Un modello di dominio (o domain model) descrive le varie entità che fanno parte di
un sistema con le loro relazioni. Questo approccio è utilizzatto nell’ingegneria del
software ed è in grado di descrivre un modello concettuale del dominio di interesse,
il quale è in grado di descrivere entità, attributi, ruoli e relazioni.
105
Capitolo 3
Il vantaggio di questo approccio è senza dubbio la grande qualità nella descrizione
di un problema e anche molto efficiente, per la coordinazione dei compiti da eseguire
in un team di elementi.
Nel caso di BAgent si possono descrivere le entità nel seguente modo:
Entità
Attributi
Cliente
indirizzo principale:
Indirizzo;
altri indirizzi:
ArrayList<Indirizzo>; nome: String; cognome:
String; contatti: Contatti; codice fiscale: String;
partita iva: String; data attivazione: Data.
Contatti
telefono: String; cellulare: String; email: String;
fax: String.
Idirizzo
via: String; numero civico: String; CAP: String;
comune: String; provincia: String; regione: String.
Appuntamento
data: Data; ora: Ora; cliente: Cliente; Indirizzo:
Indirizzo.
Prodotto
nome: String; categoria: Tipologia; opzioniTipologia: String[]; pacco: boolean; numeroElementi:
int: costo: Double; descrizione: String.
Immagine
path: String; nome immagine: String; estenzione:
String.
Ordine
data:
Data; cliente:
costo totale:
Double;
Cliente; stato:
String;
pagamento:
String;
elementi:ArrayList<Elementi>.
Elemento
quantità: int; prodotto: Prodotto.
Sconto
codice: String; casuale: String; sconto: double.
Tipologia
nome:
String;
IVA:
double;
opzioni:
ArrayList<OpzioneTipologia>.
OpzioneTipologia nome: String; campo obbligatorio: boolean.
Data
giorno: int; mese: int; anno:int.
Ora
ora: int; minuti: int.
Come si intuisce dalla tabella, gli attributi possono essere tipi semplici, oggetti,
106
Capitolo 3
o collezioni di essi. A partire da essi si possono anche ricavare le relazioni che si
vengono a creare all’interno dell’applicazione: l’inclusione di un singolo oggetto può
indicare una relazione 1..1, mentre un ArrayList di elementi, una cardinalità pari
ad N.
Nella figura sottostante è riportato il modello di dominio relativo all’applicazione.
EProdotto
EElemento
ETipologia
integra
aggiunge
riferito a
inserisce
EOrdine
−data
EOpzioneTipologia
EImmagine
EData
usa
si riferisce a
aggiunge
implementa
EOpzioneTipologiaProdotto
riferito a
EOra
ESconto
EAppuntamento
implementa
EIndirizzo
riferito a
riferito a
EContatti
aggiunge
EOpzioni
ha
ECliente
Figura 3.2: Modello di dominio dell’applicazione BAgent.
107
Capitolo
4
Analisi dei casi d’uso e SSD
In questo capito vengono descritti gli SSD (System Sequence Diagram). Si crerà un
diagramma di sequenza per l’applicazione BAgent e a partire da esso si descriverà
il codice Java per la gestione degli utenti.
4.1
System Sequence Diagram
Un Sequence Diagram è un diagramma utilizzato per descrivere uno scenario, il
quale rappresenta una sequenza di azioni predefinite. In genere è possibile creare
più diagrammi delle sequenze per una sola applicazione, ognuno dei quali è in grado
di descrivere un determinato caso d’uso.
Il Sequence Diagram descrive le relazioni che intercorrono tra gli Attori che si
vogliono rappresentare. Il messaggio è sincrono se l’emittente rimane in attesa di
una risposta, o asincrono, nel caso in cui l’emittente non aspetta nessuna risposta
ma può continuare con altre operazioni. Un messaggio generato in risposta ad uno
precedente è detto messaggio di risposta, mentre un messaggio in cui il ricevente è
nello stesso tempo l’emittente è detto ricorsivo.
Un diagramma di sequenza può essere letto in due modi; in orizzontale e in
verticale.
• in verticale (asse temporale) si mostra l’istante di tempo in cui un oggetto
viene chimato, muore o effettua una chiamata;
108
Capitolo 4
• in orizzontale si mostrano gli oggetti e le operazioni che compiono.
Gli SSD vengono rappresentati graficamente nel seguente modo:
istanze di classi Rappresentate da rettangoli con il nome della classe e l’identificatore dell’oggetto, oppure semplicemente con un nome che contraddistingue
un’istanza della classe.
Attori Sono riportati sulla sinistra con frecce di interazione verso oggetti del sistema; possono non essere riportati nel caso in cui lo scenario viene avviato a
sua volta in un’altro contesto.
Messaggi Rappresentati mediante frecce tra un attore ed un oggetto o fra due
oggetti. I messaggi di ritorno (se riportati) hanno una linea tratteggiata, quelli
sincroni terminano con una freccia triangolare piena, mentre quelli asincroni
terminano con una freccia semplice.
L’ordine dei messaggi (dall’alto verso il basso) indica la sequenza con il quale
essi vengono scambiati.
4.2
Il nostro caso d’uso: Aggiungere un Cliente
Per capire le interazioni che avvengono tra il database Db4o ed Android, si fornisce
la struttura per la creazione del caso d’uso “aggiungi cliente”. Il modo migliore
per descrivere le operazioni che avvengono in questa situazione è senza dubbio la
creazione del diagramma di sequenza. Le interazioni di un utente con il sistema
possono racchiudersi in:
• Aggiunta dettagli Cliente.
• Aggiunta Indirizzo Principale.
• Aggiunta Recapiti Cliente.
Nella fugura 4.1 è stato descritto l’SSD del nostro caso d’uso. Come si vede
avvengono diverse interazioni tra l’attore e il sistema, le quali saranno descritte nel
dettaglio nelle prossime sezioni.
109
Capitolo 4
sd: Campionario
Attore :
Controller :
View :
Foundation :
. Aggiungi Indirizzo principale
. Aggiungi Indirizzi secondari
. Aggiungi Recapito
1) . Aggiungi Cliente
2) . Recupera Elementi
2) Restituisci Elementi
. controllaValori
. store
1) Verifica Aggiunta
Figura 4.1: Diagramma di sequenza per il caso d’uso: Aggiungi Cliente.
4.2.1
Aggiungere un Indirizzo
Per aggiungere un indirizzo è possibile creare un nuovo diagramma di stato nel quale
si evincono le relazioni tra l’attore principale ed Android. Nello specifico è possibile
verificare le operazioni di recupero degli elementi relativi dai contatti telefonici.
Codice relativo
Per ogni indirizzo è possibile recuperare gli elementi direttamente dalla rubrica,
visualizzando la lista dei contatti mediante una Activity Built-in. Le righe di codice
per richamare l’attività descritta in precedenza sono le seguenti
I n t e n t i n t e n t = new I n t e n t ( I n t e n t . ACTION PICK,
C o n t a c t s C o n t r a c t . Contacts .CONTENT URI ) ;
s t a r t A c t i v i t y F o r R e s u l t ( i n t e n t , CONTACT RESULT) ;
nel quale sono state utilizzate le costanti statiche Intent.ACTION PICK ed ContactsContract.Contacts.CONTENT URI e il metodo startActivityForResult, indica110
Capitolo 4
sd: Campionario
Attore :
Controller :
View :
1) . Importa Contatti da Rubrica
1) Restituisci valori Contatto
2) . salva
3) . recuperaValoriInseriti
3) RestituisciValori
. VerificaValori
2) ritornaRisultato
Figura 4.2: Diagramma di sequenza per il caso d’uso: Aggiungi Indirizzo Principale.
111
Capitolo 4
to per ritornare il contatto da cercare (che sarà selezionato).
Avendo selezionato il contatto desiderato, si deve effettuare un override del
metodo onActivityResult per reperire i dati dell’elemento selezionato.
p r o t e c t e d v o i d o n A c t i v i t y R e s u l t ( i n t requestCode ,
i n t r e s u l t C o d e , I n t e n t data )
{
s w i t c h ( r eq u e s t C o d e )
{
c a s e CONTACT RESULT:
i f ( r e s u l t C o d e == RESULT OK)
{
g e s t i s c i I n d i r i z z i ( data ) ;
}
}
}
In questo codice è stata creata una struttura switch per identificare lo specifico
risultato mediante una costante intera associata (CONTACT RESULT). Si rendono
necessarie le precedenti righe perchè ogni Activty è in grado di collaborare con le
altre, fornendo e ricevendo informazioni attraverso il metodo startActivityForResult,
gli Intent e le costanti statiche che identificano l’elemento che sta ritornando.
Per completare la trattazione relativa alla creazione di un indirizzo, si mostra
come è stato creato il metodo gestisciIndirizzi, all’interno del quale vengono recuperati valori come cap, numero civico, città, paese ecc.
Per eseguire il comando è necessario effettuare una nuova query con URI definita
in ContactsContract.Data.CONTENCT URI e creare un ciclo che ha un numero di
ripetizioni pari agli indirizzi recuperati del determinato utente (casa, lavoro, ecc.).
Uri c o nt a ct Da t a = data . getData ( ) ;
Cursor c r = managedQuery ( contactData , n u l l , n u l l , n u l l ,
null );
i f ( c r . moveToFirst ( ) )
{
String contactId = cr . getString (
112
Capitolo 4
c r . getColumnIndexOrThrow ( C o n t a c t s C o n t r a c t . Contacts
. ID ) ) ;
Cursor p o s t a l s = g e t C o n t e n t R e s o l v e r ( ) . query (
C o n t a c t s C o n t r a c t . CommonDataKinds . S t r u c t u r e d P o s t a l
. CONTENT URI, n u l l ,
C o n t a c t s C o n t r a c t . CommonDataKinds . S t r u c t u r e d P o s t a l
.CONTACT ID + ” = ” + c o n t a c t I d , n u l l , n u l l ) ;
i n t postFormatted= p o s t a l s . getColumnIndex (
C o n t a c t s C o n t r a c t . CommonDataKinds . S t r u c t u r e d P o s t a l
.FORMATTED ADDRESS) ;
i n t postTypeNdx= p o s t a l s . getColumnIndex (
C o n t a c t s C o n t r a c t . CommonDataKinds . S t r u c t u r e d P o s t a l
.TYPE) ;
i n t v i a= p o s t a l s . getColumnIndex ( C o n t a c t s C o n t r a c t
. CommonDataKinds . S t r u c t u r e d P o s t a l .STREET ) ;
i n t comune= p o s t a l s . getColumnIndex ( C o n t a c t s C o n t r a c t
. CommonDataKinds . S t r u c t u r e d P o s t a l . CITY ) ;
i n t cod= p o s t a l s . getColumnIndex ( C o n t a c t s C o n t r a c t
. CommonDataKinds . S t r u c t u r e d P o s t a l .POSTCODE) ;
i n t prov= p o s t a l s . getColumnIndex ( C o n t a c t s C o n t r a c t
. CommonDataKinds . S t r u c t u r e d P o s t a l .REGION ) ;
i n t pobox= p o s t a l s . getColumnIndex ( C o n t a c t s C o n t r a c t
. CommonDataKinds . S t r u c t u r e d P o s t a l .POBOX) ;
w h i l e ( p o s t a l s . moveToNext ( ) && t r o v a t o != t r u e )
{
[...]
}
postals . close ();
113
Capitolo 4
nel quale viene estratta la URI (contactData) relativa al contatto selezionato e di
conseguenza viene effettuata una Query per recuperare i dati di esso.
Ogni query restituisce un cursore e con esso si possono effettuare tipiche operazioni agli elementi restituiti. Gli interi posti prima del ciclo while sono utilizzati per
recuperare rispettivamente i dati relativi all’indirizzo completo, al tipo (principale,
secondario, ecc.), alla via, città, codice postale, regione e numero civico, mediante
il metodo getString(int) che può essere espresso ad esempio per il primo intero nel
seguente modo.
p o s t a l D a t a= p o s t a l s . g e t S t r i n g ( postTypeNdx ) ;
Inserendo la precedente riga all’interno del ciclo si possono recuperare i valori
dei vari campi relativi a tutte le altre proprietà.
Questa metolodia è stata implementata a partire dalla versione 2.0 di Android,
per questo, non è possibile installare l’appḂAgent in dispositivi che utilizzano una
versione precedente.
La scelta è stata dettata principalmente dai dati, i quali
affermano che le versioni 1.x hanno circa il 2% degli accessi al market android.
Da notare che per utilizzare la lista dei contatti è necessario l’inserimento di
un particolare premesso all’interno del file manifest, che in questo caso dichiara la
possibilità di accedere alla rubrica telefonica.
<uses −p e r m i s s i o n a n d r o i d : name=”a n d r o i d . p e r m i s s i o n
.READ CONTACT” />
4.2.2
Aggiungere Recapiti
Per aggiungere i recapiti, quali telefono fisso, mobile, email e fax, ancora una volta
è stata creata una nuova Activity adatta a questo scopo. In questo caso l’SSD è
simile al precedente ed è espresso nel seguente modo.
Per recuperare gli elementi dai contatti telefonici si rende necessaria l’inclusione
del permesso
<uses −p e r m i s s i o n a n d r o i d : name=”a n d r o i d . p e r m i s s i o n
.READ CONTACT” />
all’interno del file manifest.
114
Capitolo 4
sd: Campionario
Attore :
Controller :
View :
1) . Importa Contatti da Rubrica
1) Restituisci valori Contatto
2) . salva
3) . recuperaValoriInseriti
3) RestituisciValori
. VerificaValori
2) ritornaRisultato
Figura 4.3: Diagramma di sequenza per il caso d’uso, Aggiungi Recapiti.
115
Capitolo 4
In questo caso ogni tipologia di recapito richiede una specifica query; per questo
motivo sono trattate singolarmente.
Codice relativo: recupero id e nome
Per recuperare dati come l’ID o il nome del contatto è necessario utilizzare la URI
salvata in ContactsContract.Contacts.CONTENT URI.
ContentResolver cr = getContentResolver ( ) ;
Cursor c u r = c r . query ( C o n t a c t s C o n t r a c t . Contacts . CONTENT URI,
null , null , null , null ) ;
i f ( c u r . getCount ( ) > 0 ) {
w h i l e ( c u r . moveToNext ( ) ) {
String id = cur . g e t S t r i n g (
c ur . getColumnIndex ( C o n t a c t s C o n t r a c t . Contacts . ID ) ) ;
S t r i n g name = c u r . g e t S t r i n g (
c u r . getColumnIndex ( C o n t a c t s C o n t r a c t . Contacts
. DISPLAY NAME ) ) ;
i f ( I n t e g e r . p a r s e I n t ( c u r . g e t S t r i n g ( c ur . getColumnIndex
( C o n t a c t s C o n t r a c t . Contacts .HAS PHONE NUMBER))) > 0 )
{
[...]
}
}
}
Nel precedente codice si è creata una istanza di ContentResolver e successivamente
è stato utilizzata questa istanza per effetturare la query, la quale ritorna un Cursore
contenente la lista dei contatti.
Il successivo passo eseguito verifica se l’iteratore contiene elementi, e in tal caso
effettua un ciclo dove il primo valore (id ) è un ID identificativo dell’utente corrente, mentre la seconda variabile (name) contiene il nome associato. L’ultima
istruzione verifica se l’utente corrente (corrispondente alla posizione attuale del cursore) ha o meno un numero di telefono, attraverso l’istruzione if e la costante statica
ContactsContract.Contacts.HAS PHONE NUMBER.
116
Capitolo 4
Codice relativo: recupero telefono fisso
Come per altri dati relativi ad un contatto, i numeri di telefono sono contenuti in
una tabella separata, quindi hanno bisogno di una seconda query. Per effettuare
questa ricerca è necessario utilizzare la URI contenuta all’interno della variabile
ContactsContract.CommonDataKind.Phone.CONTENT URI.
All’interno del codice viene utilizzato un ciclo While per recuperare il numero di
uno specifico contatto mediante un ID (definito nella precedente sezione).
i f ( I n t e g e r . p a r s e I n t ( c u r . g e t S t r i n g ( c u r . getColumnIndex (
C o n t a c t s C o n t r a c t . Contacts .HAS PHONE NUMBER) ) ) > 0 ) {
Cursor pCur = c r . query (
C o n t a c t s C o n t r a c t . CommonDataKinds . Phone . CONTENT URI,
null ,
C o n t a c t s C o n t r a c t . CommonDataKinds . Phone .CONTACT ID +
” = ?” ,
new S t r i n g [ ] { i d } , n u l l ) ;
w h i l e ( pCur . moveToNext ( ) ) {
[...]
}
pCur . c l o s e ( ) ;
}
Codice relativo: recuperare numeri secondari
Da precisare che ogni contatto può contenere diversi numeri di telefono. Questi
possono essere numeri fissi, mobili, fax, ma anche numeri secondari appartenenti
alle precedenti categorie.
In questo secondo caso si evidenzia la tipologia del numero ed il numero stesso
attraverso attrettante variabili (number e type).
i f ( phone . moveToFirst ( ) ) {
f i n a l i n t contactNumberColumnIndex= phone
. getColumnIndex ( Phone .NUMBER) ;
f i n a l i n t contactTypeColumnIndex= phone
117
Capitolo 4
. getColumnIndex ( Phone .TYPE) ;
w h i l e ( ! phone . i s A f t e r L a s t ( ) ) {
f i n a l S t r i n g number= phone
. g e t S t r i n g ( contactNumberColumnIndex ) ;
f i n a l i n t type= phone . g e t I n t ( contactTypeColumnIndex ) ;
f i n a l i n t t y p e L a b e l R e s o u r c e = Phone
. getTypeLabelResource ( type ) ;
doSomethingWithAContactPhoneNumber ( number ,
typeLabelResource ) ;
phone . moveToNext ( ) ;
}
}
phone . c l o s e ( ) ;
Codice relativo: recuperare email
Anche per recuperare le email è necessario eseguire una query dove la URI relativa
a queste è salvata in ContactsContract.CommonDataKinds.Email.CONTENT URI.
Cursor emailCur = c r . query (
C o n t a c t s C o n t r a c t . CommonDataKinds . Email . CONTENT URI,
null ,
C o n t a c t s C o n t r a c t . CommonDataKinds . Email .CONTACT ID
+ ” = ?” ,
new S t r i n g [ ] { i d } , n u l l ) ;
w h i l e ( emailCur . moveToNext ( ) ) {
S t r i n g e m a i l = emailCur . g e t S t r i n g (
emailCur . getColumnIndex ( C o n t a c t s C o n t r a c t . CommonDataKinds
. Email .DATA) ) ;
S t r i n g emailType = emailCur . g e t S t r i n g (
emailCur . getColumnIndex ( C o n t a c t s C o n t r a c t . CommonDataKinds
. Email .TYPE) ) ;
}
118
Capitolo 4
emailCur . c l o s e ( ) ;
}
4.2.3
Aggiungere un Cliente
Una volta aggiunti gli Indirizzi ed i Recapiti è possibile inserire i dati relativi ad
un Cliente come nome, cognome, codice fiscale e in un secondo momento effettuare
l’inserimento all’interno del database Db4o.
Questa struttura può essere rappresentata all’inteno di un diagramma SSD, il
quale è lo stesso introdotto all’inizio del capito.
sd: Campionario
Attore :
Controller :
View :
Foundation :
. Aggiungi Indirizzo principale
. Aggiungi Indirizzi secondari
. Aggiungi Recapito
1) . Aggiungi Cliente
2) . Recupera Elementi
2) Restituisci Elementi
. controllaValori
. store
1) Verifica Aggiunta
Figura 4.4: Diagramma di sequenza per il caso d’uso: Aggiungi Cliente.
Codice relativo
Avendo già chiamato le Activity corrispondenti alla creazione di un indirizzo o un
recapito esposte in precedenza, è possibile salvarne i risultati all’interno del metodo
onActivityResult.
119
Capitolo 4
@Override
p u b l i c v o i d o n A c t i v i t y R e s u l t ( i n t requestCode , i n t r e s u l t C o d e ,
I n t e n t data )
{
s u p e r . o n A c t i v i t y R e s u l t ( requestCode , r e s u l t C o d e , data ) ;
switch ( requestCode )
{
//AGGIUNGI INDIRIZZO
c a s e (ADD IND ) : {
i f ( r e s u l t C o d e == A c t i v i t y . RESULT OK)
{
EInd = ( E I n d i r i z z o ) b r i d g e
. getParameter ( ” E I n d i r i z z o ” ) ;
}
break ;
}
//AGGIUNGI RECAPITI
c a s e (ADD REC) : {
i f ( r e s u l t C o d e == A c t i v i t y . RESULT OK)
{
ECont = ( EContatti ) b r i d g e . getParameter ( ” EContatti ” ) ;
}
break ;
}
Il precedente codice verifica quale attività sta tornando mediante lo switch e se essa
ritorna un risultato positivo attraverso la costante Activity.RESULT OK. In questo
caso si instanzia il relativo oggetto con il valore ripreso dalla classe Singleton. Si
noti che in questo caso gli oggetti Indirizzo o Contatti non sono stati ancora inseriti
nella base dati. Essi sono solo salvati in memoria utilizzando il pattern specifico.
La precedente affermazione dimostra un’ulteriore proprietà della classe implementata per lo scambio di oggetti univoci tra Activity, la quale non è possibile con
120
Capitolo 4
le API messe a disposizione dai progettisti Andorid.
Una volta recuperati i dati relativi ad Indirizzi e Contatti ed avendo inserito gli
elementi di un utente è possibbile salvarlo all’interno del database con il seguente
metodo.
p r i v a t e View . O n C l i c k L i s t e n e r v e r i f i c a A g g i u n t a=new View
. OnClickListener () {
p u b l i c v o i d o n C l i c k ( View v ) {
S t r i n g [ ] r e t= VAggCl . r e c u p e r a V a l o r i E d i t i T e x t ( etNome ,
etCognome , etCf , e t P i v a ) ;
i n t v a l u e= c o n t r o l l a V a l o r i ( r e t [ 0 ] , r e t [ 1 ] ,
r e t [ 2 ] , r e t [ 3 ] , EInd , ECont ) ;
i f ( v a l u e ==0)
{
E C l i e n t e EC= new E C l i e n t e ( r e t [ 0 ] , r e t [ 1 ] , ECont ,
EInd , EDate , r e t [ 3 ] , r e t [ 2 ] ) ;
f c . s t o r e (EC ) ;
s e t R e s u l t (RESULT OK ) ;
finish ();
}
}
};
Lo specifico metodo, attivato alla pressione del pulsante di conferma, recupera prima i dati relativi al cliente attraverso la classe VAggiungiCliente e poi effettua una
serie di controlli sui dati con delle specifiche Regular Expression. Se non si riscontrano errori l’oggetto Cliente è inserito all’interno del database attraverso il metodo
fc.store(EC), dove EC è il valore da salvare, mentre fc è un oggetto foundation
relativo al cliente (FCliente), il quale si occupa dell’interazione con il database per
gli oggetti Cliente attaverso la classe generica Foundation.
Il metodo store può essere creato effettuando una chiamata al metodo db() della superclasse Foundation, il quale restituisce un oggetto di tipo ObjectContainer
121
Capitolo 4
utilizzato per accedere ai dati inseriti nella base dati, mentre la commit() rende
l’oggetto persistente all’interno di esso.
public void s t o r e ( ECliente e c l )
{
db ( ) . s t o r e ( e c l ) ;
db ( ) . commit ( ) ;
}
Molto esaltante il fatto che l’oggetto Cliente contiene al suo interno sia Indirizzi
che Recapiti; mediante le precedenti righe di codice verranno salvati tutti gli elementi contenuti all’interno di esso, diversamente dai database relazionali nei quali è
necessario effettuare diverse operazioni di insert per ogni elemento da inserire.
Come specificato in precedenza, db() è un metodo della superclasse Foundation
e può essere implemtentato nel seguente modo.
p r o t e c t e d O b j e c t C o n t a i n e r db ( )
{
try
{
i f ( oc==n u l l | | oc . e x t ( ) . i s C l o s e d ( ) )
oc= Db4o . o p e n F i l e ( dbConfig ( t h i s . o b j e c t ) ,
db4oDBFullPath ( c o n t e x t ) ) ;
r e t u r n oc ;
}
c a t c h ( E xc e pt i o n e )
{
Log . e ( t h i s . path , e . t o S t r i n g ( ) ) ;
return null ;
}
}
Nel precedente codice viene creato un nuovo oggetto di tipo ObjectContainer solo
nel caso in cui non è presente in memoria. Questa situazione riduce i costi associati
alla sua creazione.
122
Capitolo 4
4.3
Modificare ed eliminare un Cliente
Operazioni di modifica ed eliminazione dei Clienti sono anche esse molto semplici
utilizzando un database ad oggetti. I prossimi paragrafi sono dedicati alla modifica
ed all’eliminazione di un cliente, inserito con i costrutti descritti in precedenza.
In questi casi, i diagrammi di sequenza possono essere molto simili tra loro, nei
quali la prima operazione da eseguire è il recupero dello specifico oggetto, mentre la
seconda è la modifica o l’eliminazione di esso.
sd: Campionario
Attore :
Controller :
Foundation :
1) . Seleziona Cliente
2) . Recupera Cliente
2) Ritorna Cliente
1) Visualizza Cliente
3) . Esegui Operazione
. store/delete
3) Notifica Attore
Figura 4.5: Diagramma di sequenza per il caso d’uso: Modifica o Elimina Cliente.
4.3.1
Modificare un Cliente
La modifica dei clienti può essere pensata in modo molto simile alla creazione. In
questo caso basta recuperare l’oggetto desiderato ad esempio mediante la seguente
123
Capitolo 4
query SODA (adatta per la creazione di query dinamiche come nel nostro caso)
inclusa nella classe FCliente.
p u b l i c A r r a y L i s t <ECliente > r e t r i v e C o g n o m i C l i e n t i ( S t r i n g c )
{
A r r a y L i s t <ECliente > e c= new A r r a y L i s t <ECliente > ( ) ;
ObjectSet <Object> o s r e s= s u p e r . findByName ( c , t h i s . cognome ,
order , l i k e ) ;
w h i l e ( o s r e s . hasNext ( ) )
e c . add ( ( E C l i e n t e ) o s r e s . next ( ) ) ;
return ec ;
}
Il precedente metodo ritorna un ArrayList poichè il cognome da cercare (unico
parametro passato) potrebbe essere contenuto più volte all’interno della base dati.
Il risultato di una query è sempre espressa mediante un oggetto di tipo ObjectSet, mentre super.findByName(c, this.cognome, order, like) è un metodo della
superclasse Foundation che può essere espresso nel seguente modo.
p u b l i c ObjectSet <Object> findByName ( S t r i n g name , S t r i n g des )
{
Query query= db ( ) . query ( ) ;
query . c o n s t r a i n ( t h i s . o b j e c t ) ;
query . descend ( de s ) . o r d e r D e s c e n d i n g ( )
. c o n s t r a i n ( new S t r i n g ( name ) ) ;
r e t u r n query . e x e c u t e ( ) ;
}
Si noti l’enorme potenzialità della struttura. Il metodo findByName(String name,
String des) può essere utilizzato oltre alla ricerca per cognome di un Cliente, anche
per ricercare un ordine, un prodotto ecc.
124
Capitolo 4
Dopo aver recuperato gli elementi è possibbile effettuare la chiamata al metodo
update di FCliente per rendere persistenti i cambiamenti.
p u b l i c v o i d update ( E C l i e n t e EC1 , E C l i e n t e EC2)
{
EC1 . s e t C o d i c e F i s c a l e (EC2 . g e t C o d i c e F i s c a l e ( ) ) ;
EC1 . setCognome (EC2 . getCognome ( ) ) ;
EC1 . s e t D a t a N a s c i t a (EC2 . g e t D a t a N a s c i t a ( ) ) ;
EC1 . s e t I n d i r i z z o (EC2 . g e t I n d i r i z z o ( ) ) ;
EC1 . setNome (EC2 . getNome ( ) ) ;
EC1 . s e t P a r t i t a I v a (EC2 . g e t P a r t i t a I v a ( ) ) ;
EC1 . s e t C o n t a t t i (EC2 . g e t C o n t a t t i ( ) ) ;
i f (EC2 . r e t r i v e A l l A l t r i I n d i r i z z o ( ) != n u l l )
EC1 . s e t A l l A l t r i I n d i r i z z i (EC2
. retriveAllAltriIndirizzo ());
s t o r e (EC1 ) ;
}
Il significato di questo codice è molto semplice. Vengono passati un oggetto
Cliente (EC1) da modificare ed un oggetto Cliente (EC2) contenente i nuovi valori
da inserire all’interno del primo.
Le chiamate ai metodi setxx() sono quelle tipiche dei linguaggi di programmazione ad oggetti, per cui mediante Db4o si ha una perfetta integrazione con
esso (Java in questo caso).
L’ultima riga è rappresentata dal metodo store(), il quale è lo stesso utilizzato
per la creazione di un nuovo oggetto nel database. Db4o, internamente, riesce a
verificare che si tratta di un elemento già inserito, quindi, effettua solo la modifica
dei valori al suo interno.
4.3.2
Eliminare un Cliente
L’eliminazione di un cliente è del tutto simile alla sua modifica. Anche in questo
caso si deve recuperare un oggetto o una lista di essi per poi effettuare la chiamata
125
Capitolo 4
al metodo delete() della super classe Foundation.
p u b l i c v o i d d e l e t e ( Object o )
{
db ( ) . d e l e t e ( o ) ;
db ( ) . commit ( ) ;
}
Il parametro passato all’interno del metodo è un Object generico, quindi implica che
il metodo è utilizzabile in ogni contesto dell’applicazione.
Le righe di codice inserite al suo interno hanno gli stessi significati discussi in precedenza. db() recupera un ObjectContainer, mentre il metodo commit()
rende persistenti le modifiche, quindi in questo modo si effettua la chiusura di una
transizione.
126
Capitolo
5
Conclusioni
5.1
Il problema
Durante la stesura della tesi, si è più volte scritto che esistono diversi sistemi per
dispositivi mobili. Alla luce di quanto espresso nei primi capitoli, appare chiaro
che ogni programmatore potrebbe predilire la programmazione in teconologie open
come Android. Allo stesso modo, anche gli utenti finali potrebbero trovarsi davanti una vasta gammma di prodotti tra cui scegliere, con un sistema performante,
dalle innumerevoli estensioni e dai costi, in alcuni casi molto ridotti rispetto agli
antagonisti.
Nell’applicazione cui si è discusso negli ultimi due capitoli della tesi, si è posto
di risolvere il problema generico riguardante la gestione di un campionario di merci, prodotti, clienti ed appuntamenti. Per il precedente motivo è stato necessario
scegliere un sistema per la gestione della persistenza dei dati.
5.2
La soluzione
La maggior parte delle applicazioni scritte per Android o per qualsiasi smartphone
hanno bisogno di un database di tipo embedded, cioè che non necessita di una
connessione client server per accedere ai dati in esso contenuti. Questa caratteristica
è tipica di SQLite, Db4o e Perst.
127
Capitolo 5
Per la gestione della persistenza dei dati, essendo Android scritto con un linguaggio di programmazione Pure Object, Java, è stata naturale la scelta di un DBMS
orientato ad oggetti. Come si è visto, un OODBMS riduce le righe di codice utilizzate per accededere ai dati contenuti nel database e per la conversione tra un
modello ad oggetti (Android) in uno relazionale (RDBMS ), tipicamente realizzato
mediente un ORM.
Sicuramente il successo di un sistema per dispositivi mobili è dato dal numero
di estensioni o applicazioni disponibili. Ad avvalere questa affermazione, gli stessi
svilupatori Android hanno fornito tool e plugin per la loro realizzazione. L’applicazione di riferimento della tesi, BAgent, è stata realizzata mediante l’IDE Eclipse ed
i plugin OME (Object Manager Enterprise), utilizzato per Db4o, ed ADT (Android
Development Tool), usato per lo sviluppo dell’applicazione Android.
Per la creazione dei codici si è fatto uso dei Patterns MVC e Singleton, i quali mi
hanno aiutato a definire varie classi per la suddivisione dei compiti e per la gestione
di oggetti univoci per la comunicazione all’interno del progetto e del database.
5.3
considezioni personali
Gli argomenti principali di questo testo sono Android e i DBMS. Per ognuno di essi
esprimo di seguito un mio parere.
Android e smartphone. Di solito la scelta di quale dispositivo mobile comprare non avviene a seconda del sistema su cui si basa, ma in base alla conoscenza
che le persone hanno di quello smartphone. Questa situazione porta di solito il
cliente a comprare un prodotto a scatola chiusa, di cui non si conoscono le reali
caratteristiche e il mondo che gira attorno ad esso.
Una differenza che si può trovare nei diversi sistemi, sta nella varietà di prodotti
tra cui scegliere. L’associazione partner di Google (OHA) conta costruttori di dispositivi mobili e mobile carriers, al contrario di altri, che fanno capo ad una singola
azienda. La precedente affermazione non si traduce però in termini di guadagno,
infatti da un indagine risulta che Apple guadagna più dell’insieme di tutte le aziende
che utilizzano Android nei propri device.
128
Capitolo 5
Programmare nei diversi sistemi per dispositivi mobili risulta essere molto diverso. In Android non sono necessarie royalities ed è possibile inserire liberamente le
applicazioni nel market. Se si vuole programmare ad esempio per Iphone, il prodotto
deve essere testato più volte prima di ricevere l’ok per includerlo nello store.
Dalle precedenti righe si capisce che uno degli aspetti fondamentali nella programmazione e nella realizzazione di smartphone, come accade in ogni campo,
riguarda costi e profitti. I prodotti che utilizzano il sistema Google possono partire
da poche centinaia di euro, mentre altri dispositivi hanno un costo molto elevato.
Forse una delle ragioni di questo gap è proprio da implicare alla natura dei sistemi
in essi contenuti.
L’informatica è una materia di studio che ha pochi anni rispetto altre, ma è una
delle poche che fornisce prodotti open. Secondo il mio parere, gli stessi programmatori dovrebbero favorirne la crescita e stimolare l’evoluzione di questi elementi e di
Android nel nostro contesto.
OODBMS e RDMBS. Gli OODBMS si associano sempre ai linguaggi di
programmazione orientati agli oggetti. Siccome questi sono arrivati in seguito ai
linguaggi procedurali, si è dovuto necessariamente incorporare le strutture per la
memorizzazione dei dati attraverso gli RDBMS, i quali hanno avuto un successo
strabiliante. Negli ultimi anni sembra che qualcosa stia cambiando. Stanno nascendo molti sistemi per la gestione di base dati ad oggetti e questo fa ben sperare per il
futuro. L’unica incognita è ancora legata a fattori economici. Una grande azienda
sarà disposta a cambiare il proprio RDBMS verso un ODBMS ?.
In applicazioni Android il database di riferimento è SQLite, il quale è stato
incluso all’interno dello stack. Per contro, la teconologia utilizzata in BAgent è
stato un ODBMS che va sotto il nome di Db4o. Esso è un sistema reperibile sotto
licenza open-source caratterizzato dall’essere di tipo embedded, cioè non necessita
di connessioni client-server per l’accesso ai dati in esso contenuti.
Il secondo sistema per la gestione dei dati trattato nella tesi è Perst. Apparentemente ha le stesse caratteristiche incontrate in Db4o, ma al suo interno le cose cambiano molto. Per ogni operazione da eseguire bisogna sempre far riferimento ad un
particolare oggetto. Personalmente preferisco l’approccio adottato in Db4o, il quale
introduce un livello maggiore di trasparenza verso il programmatore, diminuendo i
129
Capitolo 5
codici e di conseguenza i possibili errori.
Il processo precedentemente esposto porta a minori tempi di latenza nel momento
in cui si effettuano operazioni di inserimento, ricerca ed eliminazione su milioni o
miliardi di oggetti. Questo proposito potrebbe far cambiare del tutto la mia opinione
sui due sistemi, ma dovendo implementare i due in un dispositivo Android, potrebbe
essere altamente improbabile dover operare con una cosı̀ grande quantità di oggetti.
Per questo motivo si vuol dare grande merito agli sviluppatori di Perst che sono
riusciti a far ottenere delle performance quasi sorprendenti a discapito di un maggiore
sforzo nella creazione dei codici.
5.4
sviluppi futuri
Riguardo BAgent potrebbe essere utile nel futuro l’adattamento dell’applicazione
per i tablet. In questo caso basterebbe modificare i file .xml, migliorando i layout
per i device in questione. Ulteriori sviluppi potrebbero riguardare la gestione del
sistema di localizzazione GPS. Ormai quasi tutte le applicazioni scritte in Android
fanno riferimento a questo sistema. In un’applicazione come BAgent, in grado di
gestire appuntamenti con i clienti, potrebbe essere molto efficente la localizzazione
su mappa di dove questi avverranno.
Un ulteriore sguardo al futuro potrebbe riguardare una gestione centralizzata
dei dati mediante il sistema DRS (Data Replication System) introdotto da Db4o.
Questa tecnica è in grado di replicare i dati all’interno del database del dispositivo,
in uno sito su di un server, mediante una connessione TCP/IP. Con questa caratteristica l’applicazione potrebbe essere utilizzata all’interno di aziende che hanno
un database centralizzato (da creare o addirittura già esistente) con molti agenti
di commercio. Ogni utente che utilizzerà l’applicazione potrà essere in grado di
salvare nuovi elementi nel database interno e in un secondo momento, inviare dati
al server centrale, in modo da eliminare eventuali problemi di connessione e per la
condivisione dei dati.
130
Ringraziamenti
Il momento più bello durante il lungo viaggio di scrittura di una tesi è sicuramente
quello in cui arrivi ai rigraziamenti. In queste situazioni ripercorri gli istanti che ti
hanno portato ad un gradino dalla tanto ambita laurea in ingegneria informatica.
Per questa ragione ho intenzione di ringraziare la mia famiglia, che mi ha permesso di arrivare fin qui, ed i miei amici, cui non scriverò i loro nomi singolarmente,
poichè leggendo queste righe capiranno di esserre in questa stretta cerchia.
Un ulteriore ringraziamento va a tutte le persone che mi sono state vicine nel
periodo in cui ho frequentato l’università, mentre, dopo essere ritornato nel mio
paese, mi hanno ospitato per pranzi, caffè ed intere notti.
131
Bibliografia
[1] Bitmap.it, Mercato smartphone. http://www.bitmat.it/articolo.php?aId
=0000090444&cId=46&cpId=20&n=Dispositivi+mobili:+un+mercato+in+
crescita+costante.
[2] Delvecchio
A.
2011,
sistemi
per
dispositivi
mobili.
http://www.iphoneitalia.com/nel-panorama-dei-dispositivi-mobili-google-edapple-continuano-a-mantenere-un-dominio-quasi-incontrastato-289690.html.
[3] Nittoli
L.
2011,
sistemi
per
dispositivi
mobili
(2).
http://www.androidworld.it/2011/11/20/un-grafico-mostra-landamento-dellevendite-globali-degli-smartphone-e-la-straordinaria-crescita-di-android-63262/.
[4] Meier, R. 2010, Professional Android 2 - Application Development. Wiley
Puplishing, Inc. Indiana.
[5] Carli, M. 2010, Android - Guida per lo sviluppatore, Apogeo.
[6] Google,
2012,
Documentazione
ufficiale
per
gli
sviluppatori.
http://developer.android.com/guide/basics/what-is-android.html.
[7] Google, 2012, Android Target Device. http://developer.android.com/resources
/dashboard/platform-versions.html.
[8] Di Felice P. 2008, Appunti base dati uno - RDBMS.
[9] SQLite, Documentazione ufficiale per gli sviluppatori. http://www.sqlite.org.
132
Capitolo 5
[10] ODBMS.org, 2012, ODBMS. http://www.odbms.org/.
[11] Paterson J., Edlich S., Hrning H., Hrning R. 2006, The definitive guide to Db4o.
Apress.
[12] Versant,
2012,
Documentazione
ufficiale
per
gli
sviluppatori
Db4o.
http://community.versant.com/.
[13] Versant, 2012, DRS - Db4o. http://www.db4o.com/about/productinformation/
drs/.
[14] Polepos, 2012, Benchmarks database. http://www.polepos.org/.
[15] McObject,
2012,
Documentazione
ufficiale
http://www.mcobject.com/perst.
[16] Bigatti M., il linguaggio Java, Hoepli informatica.
133
per
gli
sviluppatori.
Appendice
A
Programazione ad oggetti e Java
A.1
Programmazione ad oggetti
Il paradigma ad oggetti ha cambiato il modo di rappresentare le identità all’interno di un’applicazione. In questo tipo di programmazione (OOP, Object Oriented
Programming) vengono create classi 1 , le quali sono rappresentate da un modello.
A partire dal modello vengono creati più oggetti 2 , diversi tra loro, ma che hanno
la stessa struttura. Per esempio, per instanziare molti oggetti di tipo Impiegato
bisogna utilizzare una classe Impiegato. Ogni oggetto dello stesso tipo corrisponderà
ad un istanza della classe differente da qualsiasi altra.
Obiettivo fondamentale di questa programmazione è la riusabilità del codice.
Avendo creato una specifica classe, essa può essere utilizzata più volte all’interno
dell’applicazione, oltre ad essere esportabile in altre. Ogni oggetto è caratteristico
di una propria entità, in modo da distinguerlo dai propri simili. Questo meccanismo
è offerto dalla notazione di maniglia (handle) dell’oggetto, nota con il nome formale
di identificatore o Object ID.
Ogni classe è caratterizzata da metodi ed attributi. Gli attributi sono elementi
caratteristici di un determinato oggetto come ad esempio il nome per una Persona.
1
Una classe è la struttura dalla quale vengono creati oggetti all’iterno
dell’applicazione
2
Gli oggetti possono essere paragonati ad entità fisiche create a partire dalle
classi, all’esecuzione dell’applicazione.
134
Appendice A
Gli attributi possono rappresentare tipi semplici, oggetti o collezione di essi.
Un metodo può essere paragonato ad una funzione di un linguaggio procedurale
3
atto a lavorare con gli attributi dell’oggetto corrispondente. Metodi base questo in
tipo di programmazione sono ad esempio i setxxx() e getxxx(), che rispettivamente
modificano o recuperano il valore di un determinato dato.
Mediante l’uso dei metodi viene creata una sorta di incapsulamento tra l’ambiente esterno e gli attributi degli oggetti. I soli elementi che possono accedere agli
attributi sono i metodi della specifica classe. Dal momento che solo le operazioni
dell’oggetto possono leggere ed aggiornare i dati di quest’ultimo, queste operazioni
formano un anello di protezione attorno al nucleo centrale composto dalle variabili.
Due oggetti possono comunicare tra loro attraverso l’invio di messaggi di tre tipi:
Messaggio informativo: il quale fornisce informazioni al destinatario.
Messaggio interrogativo: che richiede informazioni al destinatario.
Messaggio imperativo: che richiede di eseguire metodi al destinatario.
Gli oggetti creati all’interno delle applicazioni hanno la caratteristica di non
essere persistenti. Essi vengono cancellati dalla memoria al momento della chiusura
dell’applicazione, quindi, occorrono strumenti per consentire il salvataggio di essi
all’interno di strutture ben definite (base di dati o serializzazione).
A.2
Java
4
Java: storia e caratteristiche
è uno dei linguaggi di programmazione Object Oriented, caratterizzato da
quattro principali pecurialità:
3
Un linguaggio procedurale è formato da funzioni e sottofunzioni. Il compito di
esse è restituire dati ottenuti al suo interno, le quali non avranno più memoria di essi.
Un oggetto al contrario, è consapevole del suo passato e mantiene le informazioni al
suo interno per tutta la durata dell’applicazione.
4
Java è stato creato a partire da ricerche effettuate alla Stanford University negli
anni Novanta. Nel 1992 nasce il linguaggio Oak prodotto da Sun Microsystems. Tale
nome fu successivamente cambiato in Java a causa di un problema di copyright (il
linguaggio di programmazione Oak esisteva già). Java, oltre ad essere un linguaggio
di programmazione è un marchio registrato dalla Oracle.
135
Appendice A
• È un linguaggio Object Oriented puro 5 , di tipo type safe 6 .
• Contiene strumenti e librerie per il networking.
• È progettato per eseguire codice da sorgenti remoti in modo sicuro.
• Ha una grande portabilità.
La portabilità 7 del linguaggio è stata certemente una grande innovazione rispetto
ad altri, i quali, dopo aver ottenuto il codice sorgente, lo compilano in base alla
piattaforma.
In Java queste procedure si traducono nei seguenti punti:
• il codice viene tradotto da una copilatore in bytecode (codice intermedio)
• il bytecode viene passato ad un internprete chiamato Java Virutal Machine
(JVM).
In un primo momento Sun decise di destinare questo prodotto alla creazione di
applicazioni complesse per piccoli dispositivi elettronici, mentre nel 1993 iniziò a
farsi notare come strumento per programmare in internet. Grazie alle applet, le
pagine web diventarono interattive a livello client.
Qusto linguaggio fu inizialmente rilasciato come Java Development Kit 1.0 (JDK
8
1.0), il quale comprendeva il runtime Java (virtual machine e librerie) e gli stru-
menti di sviluppo (compilatore e altri strumenti). Successivamente venne fornito
un pacchetto che comprendeva solo il runtime chiamato Java Runtime Environment
(JRE).
5
In questo contesto, la notazione puro indica che anche i tipi semplici di dati
sono oggetti.
6
Java è generalmente considerato un linguaggio con forte tipizzazione.
7
Sebbene sia possibile scrivere in Java, programmi che si comportano in modo
consistente attraverso molte piattaforme hardware diverse, bisogna tenere presente,
che questi poi dipendono dalle macchine virtuali che sono a loro volta programmi a
sè e che hanno inevitabilmente i loro bug.
8
La Sun (ora Oracle) mette a disposizione un software development kit chiamato
Java Development Kit (JDK). Esso include un certo numero di tool di uso comune,
e altri, atti ad elaborare file sorgenti e/o compilati.
136
Appendice A
Siccome le applicazioni non sono uguali sia dal punto di vista funzionale che dal
punto di vista architetturale, sono stati definiti gli ambienti J2SE, J2EE e J2ME.
L’ambiente J2SE è usato per le applicazioni destkop composte quindi nella maggior
parte dei casi di un interfaccia utente (GUI), mentre la J2EE consente la realizzazione di applicazioni denominate enterprise, che permettono l’interattività con
sistemi di vario genere (DBMS, WebService).
La J2ME è un framework ridotto sulla base della J2SE e che viene utilizzato
per lo sviluppo di applicazioni il cui target è handeld e embedded quali smartphone,
computer palmari, telefoni cellulari, navigatori satellitari ecc. La J2ME definisce le
configuration ovvero quegli ambienti per dispositivi con caratteristiche hardware e
software simili. Al momento esistono solo due tipi di configuration:
• Connected Device Configuration (CDC): si riferisce a dispositivi con processore
a 32 bit dove la VM di riferiemento è la CDC Hotspot Implementation (nota
anche come Compact Virtual Machine).
• Connected Limited Device Configuration (CLDC): fa riferimento a dispositivi
con memoria di almeno 192 KB. La VM di riferimento prende il nome di KVM,
dove la K da l’idea delle sue dimensioni.
Per sviluppare programmi in Java è teoricamente sufficiente un qualsiasi editor di
testo; in pratica, se si vuole scrivere qualcosa di più del classico hello world occorre
un ambiente di sviluppo integrato (IDE).
137
Appendice
B
ORM ed Hibernate
Durante la stesura delle tesi si è più volte fatto riferimento agli ORM ed al fatto che
essi introducono uno nuovo livello tra l’archituttura relazionale del database e quella
ad oggetti. Per dimostrare questa situazione si effettuano alcune considerazioni Su
Hibernate.
Questo ORM ha bisogno di alcuni file di configurazione per permetterne il corretto funzionamento. Il primo di essi è il file hibernate.cfg.xml che specifica diverse
proprietà come username, password, posizione del database ecc.
Gli altri file (anche essi di estenzione .xml) effettuano il mapping tra oggetti
e tabelle del database. Come si intuisce facilmente, questi contengono le colonne
correspondenti alla rispettiva tabella e il gli attributi della classe. L’unica restrizione
richiesta con Hibernate è che ogni tabella deve avere un campo di tipo long per l’id.
138