2. Plan
Persistance de données (SQLite)
création de la base de données
mise à jour la base de données
interrogation de la base de données
Système de fichiers
stockage interne
stockage sur SD
4. Stockage de données
Dans une application Android il existe plusieurs manières de
stocker des informations:
Dans l’état sauvegardé de l’activité
Dans les préférences utilisateurs
Sous forme de fichier soit directement sur le device soit sur la carte
externe
Dans la base de données interne SQLite
En appelant des services de stockage distant
Dans cette partie nous allons voir comment interagir avec une
base de données SQLite embarquée sur le système android.
TP1
5. Stockage (clé/valeur) dans les préférences
//Dans une classe Activity
/* To get the Map of preferences*/
SharedPreferences settings = getPreferences(MODE_PRIVATE);
/* To set a preference (from an EditText field)*/
EditText e = (EditText)findViewById(R.id.username);
// get the Preferences Editor
SharedPreferences.Editor editor = settings.edit();
// change the value in the preferences
editor.putString("NAME", e.getText().toString());
editor.commit(); // be sure to commit when you're done!!
/* To get a preference (and write to a TextView) */
// get the String from the Preferences
String name = settings.getString("NAME", null);
String msg = "Welcome, guest.";
if (name != null) msg = "Welcome back, " + name + "!";
TextView t = (TextView)findViewById(R.id.welcome_msg);
t.setText(msg);
6. SQLite en quelques mots (1)
SGBDR intégré (Embedded RDBMS).
Site officiel : http://www.sqlite.org/
SQLite est efficace en terme de mémoire que
le moteur d’exécution d’Android peut l’inclure
dans son intégralité.
Taille : environ 257 Ko.
Conforme aux propriétés ACID
Atomicity, Consistency, Isolation, Durability.
7. SQLite en quelques mots (2)
SQLite permet de stocker une valeur de n’importe quel type
dans n’importe quelle colonne, quel que soit le type déclaré de
cette colonne.
SQLite ne nécessite pas de serveur pour fonctionner ce qui
n’est pas le cas de MySQL par exemple.
Pas d’architecture client/serveur.
Accessible via des appels de fonction de l'application.
L’Écriture (insert, update, delete) verrouille la base de données,
et les requêtes peuvent se faire en parallèle.
8. Base données SQLite pour android
Android ne fournit aucune base de données de son propre
chef. Si vous voulez utiliser SQLite, vous devez créer votre
propre base, puis la remplir avec vos tables, vos index et vos
données.
les bases de données sont privées à l'application qui les a
créées.
passer par un fournisseur de contenu (ContentProvider) pour les rendre
publiques
les données brutes (images, fichiers, …) ne sont généralement
pas enregistrées dans les tables (BLOB)
les noms de fichiers, chemin sont enregistrés dans la base
9. L'accès à une base de données SQLite
se fait par l'ouverture du fichier
correspondant à celle-ci
chaque base de données est enregistrée
dans un fichier qui lui est propre, avec
ses déclarations,
ses tables
ses index
et ses données.
10. SQLite - types de données
C'est tout à fait différent des types de
données SQL normales (voir le lien):
http://www.sqlite.org/datatype3.html
11. Classes de stockages
NULL : valeur null
INTEGER : entier signé, stockée dans 1, 2, 3, 4, 6 ou 8
octets selon la grandeur de la valeur
REAL : une valeur à virgule flottante, un nombre à virgule
flottante 8-byte IEEE.
TEXT : chaîne de texte, stocké à l'aide de la base de
données, encodage (UTF-8, UTF-16BE or UTF-16LE).
BLOB : La valeur est un objet blob de données, stockées
exactement comme elle était entrée.
12. Le package android.database.sqlite
Contient les classes de gestion de base de données SQLite
qu'une application utilise pour gérer sa propre base de données
privée.
SQLiteCloseable: Un objet créé à partir d'un SQLiteDatabase
qui peut être fermée.
SQLiteCursor: Une mise en œuvre de curseur qui expose les
résultats d'une requête sur une SQLiteDatabase.
SQLiteDatabase: Expose des méthodes pour gérer une base de
données SQLite.
13. Le package android.database.sqlite
SQLiteOpenHelper - une classe d'assistance pour gérer la gestion de
version et de création de base de données.
SQLiteProgram - une classe de base pour les programmes compilés
de SQLite.
SQLiteQuery - un programme de SQLite qui représente une requête
qui lit les lignes résultantes dans un CursorWindow.
SQLiteQueryBuilder - une classe pratique qui aide à construire des
requêtes SQL à envoyer aux objets SQLiteDatabase.
SQLiteStatement - une instruction précompilée contre un
SQLiteDatabase qui peut être réutilisé.
14. La classe SQLiteDatabase
Contient des méthodes pour : création, ouverture,
fermeture, insertion, mise à jour, suppression et
exécute une base de données SQLite
Ces méthodes sont similaires à JDBC mais avec
plus d’avantage que ce que nous voyons avec
JDBC (n'oubliez pas qu’il n'y a pas de serveur
SGBDR)
15. SQLiteDatabase.openOrCreateDatabase()
Cette méthode va ouvrir une base de
données existante ou en créer une dans la
zone de données d'application
i
m port androi
d.
database.
sql
i
te.
SQ Li
teD atabase;
SQ Li
t
eD at
abase m yD at
abase;
m yD at
abase = openO rCr
eat
eD at
abase ("m y_sql
i
t
e_dat
abase.
db" ,
SQ Li
t
eD at
abase.
CREATE_I
F_NECESSARY ,nul
l
);
16. android.database.sqlite.SQLiteOpenHelper
La meilleure solution pour créer et gérer une base consiste à créer une
sous classe de la classe abstraite SQLiteOpenHelper.
Il faut redéfinir les méthodes
public void SQLiteDatabase onCreate(SQLiteDatabase db)
qui est appelée si la base de données est accessible, mais pas encore
créé.
public void SQLiteDatabase onUpgrade (SQLiteDatabase db, int
odlVersion, int newVersion)
qui est appelé, si la version de la base de données est augmentée dans
votre code d'application. Cette méthode vous permet de mettre à jour un
schéma de base de données existante ou de déposer la base de données
existante et recréer par la méthode onCreate () .
où db est la base de donnée manipulée, oldVersion est la version
précédente de la base et newVersion la nouvelle version de la base.
17. Exemple de code pour l’assistante
publ
i
c cl
ass D i
cti
onaryO penH el
per extends SQ Li
teO penH el
per {
pri
vate stati
c fi
nali
nt D ATA B A SE_V ER SI
O N = 2;
pri
vate stati
c fi
nalStri
ng D I
CTI
O N A RY_TA B LE_N A M E = "di
cti
onary";
pri
vate stati
c fi
nalStri
ng D I
CTI
O N A RY_TA B LE_CR EATE =
"CREATE TABLE " + D I
CTI
O NARY_TABLE_NAM E + " (
" + KEY_W O RD + " TEXT," +
KEY_D EFI
NI
TI
O N + " TEXT)
;
";
D i
ct
i
onaryO penH el
per(
Cont
extcont
ext
){
super(context,D ATA B A SE_N A M E,nul
l
,D ATA B A SE_V ER SI
O N );
}
@ O ver
ri
de
publ
i
c voi
d onCreate(SQ Li
teD atabase db) {
db.
execSQ L(
D I
CTI
O N ARY_TABLE_CREATE)
;
}
@ O ver
ri
de
publ
i
c voi
d onU pgrade(SQ Li
teD atabase db,i
nt ol
dVersi
on,i
nt
new Versi
on) {
db.
execSQ L("D RO P TABLE I
F EXI
STS " + D I
CTI
O N A RY_TA B LE_N A M E )
;
onCr
eat
e(
db)
;
}
}
18. Propriétés de la base de données
Options importants de configuration de
base de données qui incluent : la version
locale et le verrouillage thread-safe.
i
m port j
ava.
uti
l
.
Local
e;
m yD at
abase.
set
Versi
on(
1)
;
m yD at
abase.
set
Locki
ngEnabl
ed(true);
m yD at
abase.
Set
Local
e(
Local
e.
get
D ef
aul
t
(
)
)
;
19. Création de tables
Créer une chaîne statique contenant l'instruction
SQLite CREATE, utilisez la méthode execSQL( )
pour l‘exécuter.
l'exécution de la requête ne renvoie pas de résultat
St
ri
ng cr
eat
eAut
hor= "CREAT TABLE aut
hors (
" +
"i
d I
NTEG ER PRI
M ARY KEY AUTO I
NCREM ENT,
" +
"f
nam e TEXT,
" +
"l
nam e TEXT)
;
m yD at
abase.
execSQ L(
cr
eat
eAut
hor)
;
20. insert( )
long insert(String table, String
nullColumnHack, ContentValues values)
i
m port androi
d.
content.
ContentVal
ues;
Cont
ent
Val
ues val
ues = new ContentVal
ues( );
val
ues.
put
(
"fi
rst
nam e" ,"J
.
K.
")
;
val
ues.
put
(
"l
ast
nam e" ,"Row l
i
ng")
;
l
ong new A uthorI
D = m yD atabase.
i
nsert("tbl
_authors" ,"" ,val
ues);
21. Mise à jour d'enregistrements - update( )
int update(String table, ContentValues
values, String whereClause, String[]
whereArgs)
publ
i
c voi
d updat
eBookTi
t
l
e(
I
nt
egerbookI
d,St
ri
ng new Ti
t
l
e){
Cont
ent
Val
ues val
ues = new Cont
ent
Val
ues(
)
;
val
ues.
put
(
"t
i
t
l
e" ,new Ti
t
l
e)
;
m yD at
abase.
updat
e("t
bl
_books" , val
ues ,
"i
d= ?" ,new St
ri
ng[]{bookI
d.
t
oSt
ri
ng(
)} )
;
}
22. Suppression d'enregistrements - delete( )
int delete(String table, String
whereClause, String[] whereArgs)
publ
i
c voi
d del
et
eBook(
I
nt
eger bookI
d) {
m yD at
abase.
del
et
e("t
bl
_books" ,"i
d= ?" ,
new St
ri
ng[] { bookI
d.
t
oSt
ri
ng() } );
}
24. Requêtes de sélection d'enregistrements
Méthodes query(...) de la classe
SQLiteDatabase qui exécutent des requêtes sur
la BD et renvoie les résultats dans un objet Cursor
voir la documentation
http://developer.android.com/reference/android/d
atabase/sqlite/SQLiteDatabase.html
25. Requêtes de sélection d'enregistrements
la méthode query() accepte les paramètres suivants:
distinct : booléen facultatif indiquant que le résultat doit
contenir des valeurs uniques
table : nom de la table
projection : tableau de String contenant les colonnes
selection : clause WHERE, peut inclure des paramètres (?)
selectionArgs : tableau des paramètre de la clause WHERE
groupBy : clause GROUP BY
having : clause HAVING
orderBy : clause ORDER BY
limit : clause LIMIT
26. Requêtes simples
SQL : "SELECT * FROM ABC;"
SQLite :
Cursorc = m db.
query(
abc,
nul
l
,
nul
l
,
nul
l
,
nul
l
,
nul
l
,
nul
l
);
SQL : "SELECT * FROM ABC WHERE C1=5"
SQLite :
Cursorc = m db.
query(abc,
nul
l
,
"c1= ?" ,new Stri
ng[] {"5"},
nul
l
,
nul
l
,
nul
l
);
SQL : "SELECT title,id FROM BOOKS ORDER BY title ASC"
SQLite :
St
ri
ng col
sToRet
ur
n []{"t
i
t
l
e",
"i
d"};
St
ri
ng sort
O r
der= "t
i
t
l
e ASC";
Cursorc = m db.
query(
"books",
col
sToRet
ur
n,
nul
l
,
nul
l
,
nul
l
,
nul
l
,
sortO rder);
27. Cursor, transaction
La classe Cursor gère le curseur résultat d'une sélection
récupération des valeurs de champ par des méthodes T getT(int
columnIndex) où T est le type à récupérer : getString, getInt, …
déplacements avec les méthodes de type move(...)
moveToFirst(), move(int offset), moveToNext(), …
la première colonne résultat est numéroté 0
et bien d'autres
cf. a documentation de la classe Cursor
La classe SQLiteDatabase fournit des méthode pour les
transactions:
BEGIN TRANSACTION ou BEGIN ; COMMIT;
END TRANSACTION; ROLLBACK
29. public class PersonDbAdapter extends SQLiteOpenHelper {
// column names in the table
public static final String KEY_ROWID = "_id";
public static final String KEY_NAME = "name";
public static final String KEY_AGE = "age";
// info about the database and table
private static final String DATABASE_NAME = "data";
private static final String TABLE_NAME = "names";
private static final int DATABASE_VERSION = 1;
// SQL query to create the table
private static final String TABLE_CREATE =
"create table names
(_id integer primary key autoincrement,
name text not null,
age integer not null); " ;
La classe où nous faisons tous les trucs de base de données ...
30. //Suite ..
// maintain a reference to the database object
private SQLiteDatabase db;
public PersonDbAdapter(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
// save the database object as a member variable
// this has the side effect of calling onOpen/onCreate
db = getWritableDatabase();
if (db == null) Log.v("PersonDbAdapter", "db is null!");
}
// create the database the first time it's used
public void onCreate(SQLiteDatabase d) {
d.execSQL(TABLE_CREATE);
}
// called when database is opened
public void onOpen(SQLiteDatabase d) {
super.onOpen(d);
db = getWritableDatabase();
}
31. //Toujour dans PersonDbAdapter...
/* Insert using the raw SQL */
public void insertPerson(String name, int age) {
String stmt =
"insert into names (name, age)
values ('" + name + "'," + age + ")";
db.execSQL(stmt);
}
/* Insert using key/value pairs */
public void insertPerson(String name, int age) {
ContentValues initialValues = new ContentValues();
initialValues.put(KEY_NAME, name);
initialValues.put(KEY_AGE, age);
db.insert(TABLE_NAME, null, initialValues);
}
32. /* Update using the raw SQL */
public void updatePerson(String name, int age) {
String stmt =
"update names
set age='" + age + "'
where name='" + name + "')";
db.execSQL(stmt);
}
/* Update using argument arrays */
public void updatePerson(String name, int age) {
ContentValues updateArgs = new ContentValues();
updateArgs.put(KEY_AGE, new Integer(age));
String[] whereArgs = { name };
// update returns the number of rows updated
db.update(TABLE_NAME,
updateArgs, // what to update
KEY_NAME + "=?", // where clause
whereArgs); // arguments to where clause
}
33. /* Delete using the raw SQL */
public void deletePerson(String name) {
String stmt =
"delete from names
where name='" + name + "')";
db.execSQL(stmt);
}
/* Delete using an argument array */
public void deletePerson(String name) {
String[] whereArgs = { name };
// delete returns the number of rows deleted
db.delete(TABLE_NAME, KEY_NAME + "=?", whereArgs);
}
34. /* Select using the raw SQL */
public Cursor selectPerson(String name) {
String stmt =
"select _id, name, age from names
where name='" + name + "')";
return db.rawQuery(stmt);
}
/* Select using query method */
public Cursor selectPerson(String name) {
String[] columns = { KEY_ROWID, KEY_NAME, KEY_AGE };
String[] selectionArgs = { name };
return db.query(TABLE_NAME, // table to select from
columns, // fields (columns) to
select
KEY_NAME + "=?", // WHERE clause
selectionArgs, // arguments to WHERE clause
null, // GROUP BY clause
null, // HAVING clause
null // ORDER BY clause
);
}
35. //Dans une classe Activity
// create the adapter
PersonDbAdapter adapter = new PersonDbAdapter(this);
// insert some values
adapter.insertPerson("Fred", 35);
adapter.insertPerson("Barney", 33);
// execute a query
Cursor c = adapter.selectAll();
if (c != null) {
// let Android handle the Cursor lifecycle
startManagingCursor(c);
while (c.moveToNext()) { // go to the next row
int age_i = c.getColumnIndex(PersonDbAdapter.KEY_AGE);
int age = c.getInt(age_i);
int name_i =
c.getColumnIndex(PersonDbAdapter.KEY_NAME);
String name = c.getString(name_i);
...
}
c.close();
}
37. Fichiers
Similaire à Java « standard »
Méthodes de Context
openFileOutput() → FileOutputStream
méthodes write(...)/ close()
openFileInput() → FileInputStream
méthodes read(...)/ close()
Suppression d'un fichier
deleteFile() supprimer le fichier
Vous pouvez voir le système de fichiers de l'émulateur
dans Eclipse :
Window → Show View → Other... → Android → File Explorer
38. publ
i
c voi
d ecri
reFi
chi
er(Vi
ew v){
byte[] datas = {1,
2,
3,
4,
5,
6,
7,
8,
9,
10};
try{
Fi
l
eO ut
put
St
r
eam out= thi
s.
openFi
l
eO utput("fi
chi
erI
nterne.
dat",
M O D E_A PPEN D );
out
.
w ri
t
e(
dat
as)
;
out
.
cl
ose(
)
;
} catch(Excepti
on e) {
Log.
e(
"Fi
chi
erI
nt
er
neAct
i
vi
t
y",
"Er
r
eursurfi
chi
er",
e)
;
}
}
publ
i
c voi
d l
i
reFi
chi
er(Vi
ew v){
try{
Fi
l
eI
nput
St
r
eam i
n = thi
s.
openFi
l
eI
nput("fi
chi
erI
nterne.
dat");
i
ntb;
do{
b= i
n.
r
ead(
)
;
Log.
i
(
"Fi
chi
erI
nt
er
neAct
i
vi
t
y",
"O ct
etl
u :"+ b)
;
}w hi
l
e(b!= -1);
i
n.
cl
ose(
)
;
} catch(Excepti
on e) {
Log.
e(
"Fi
chi
erI
nt
er
neAct
i
vi
t
y",
"Er
r
eursurfi
chi
er",
e)
;
}}
Exemple
39. Stockage sur SD
le stockage externe peut aussi être interne à l'appareil
carte SD intégrée
le stockage externe peut être un support amovible
l'utilisateur peut retirer à tout moment la carte
l'application doit tenir compte de l'absence de support
Il faut interroger l'environnement pour connaître la
disponibilité de la carte SD
méthode de la classe Environment
public static String getExternalStorageState()
renvoie l'état sous forme de String
40. <manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
Obtenir les autorisations pour le stockage externe
Pour écrire dans la mémoire externe, vous devez demander
la WRITE_EXTERNAL_STORAGE permission dans votre fichier manifeste :
Si votre application a besoin de lire le stockage externe (mais pas
écrire dessus), alors vous aurez besoin de déclarer la permission
READ_EXTERNAL_STORAGE.
<manifest ...>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
...
</manifest>
41. Constantes de la classe Environment
représentant l'état du support
MEDIA_BAD_REMOVAL - média retiré sans avoir été correctement démonté
MEDIA_CHECKING - média en cours de vérification
MEDIA_MOUNTED - média correctement monté en lecture/écriture
MEDIA_MOUNTED_READ_ONLY - média correctement monté en lecture
seule
MEDIA_NOFS - média présent, mais système de fichiers non supporté
MEDIA_REMOVED - média absent
MEDIA_SHARED - média présent, non monté et partagé en tant périphérique
USB
MEDIA_UNMOUNTABLE - média présent, mais ne peut pas être monté
MEDIA_UNMOUNTED - média présent mais non monté
42. Stockage sur SD
Un répertoire est affecté à chaque application
les fichiers de données sont répartis dans des sous répertoires :
Music, Alarms, ….
correspond à des constantes de la classe Environment
43. Stockage sur SD
La méthode getExternalFilesDir retourne
un
objet de type File représentant le sous
répertoire de données
Toutes les opérations d'écriture sur la carte
SD nécessite une permission d'écriture
dans le fichier AndroidManifest.xml
< uses-
per
m i
ssi
on
andr
oi
d:
nam e= "androi
d.
perm i
ssi
on.
W RI
TE_EXTERNAL_STO RAG E"/
>
44. Stockage sur SD
Les fichiers communs à plusieurs applications sont mis
dans une arborescence spécifique
les fichiers créés dans cette arborescences ne sont pas
supprimés même si l'application qui les a créés est désinstallée
même organisation d'arborescence en fonction du type de
données
objet de type Filereprésentant le sous-répertoire de données est
retourné par la méthode : getExternalStoragePublicDirectory
45. Exemple d'écriture sur SD (1)
publ
i
c voi
d ecri
reFi
chi
erExterne(Vi
ew v){
byte[] datas = { 1,2,3,4,5,6,7,8,9,10 };
St
ri
ng st
at
e = Envi
r
onm ent
.
get
Ext
er
nal
St
orageSt
at
e(
)
;
i
f(!state.
equal
s(Envi
ronm ent.
M ED I
A _M O U N TED )){
Log.
e(
"Fi
chi
erExt
er
neAct
i
vi
t
y","La cart
e SD n'
estpas m ont
ée en écri
t
ur
e")
;
Toast
.
m akeText
(thi
s,"La carte SD n'
est pas m ontée en écri
ture",
Toast.
LEN G TH _SH O RT).
show ();
return;
}
try{
Fi
l
e di
r= thi
s.
getExternal
Fi
l
esD i
r(Envi
ronm ent.
D I
R ECTO RY_D O W N LO A D S);
Fi
l
e fi
l
e = new Fi
l
e(
di
r,
"t
est
.
dat
a")
;
fi
l
e.
cr
eat
eNew Fi
l
e(
)
;
Fi
l
eO ut
put
St
r
eam f
out= new Fi
l
eO ut
put
St
r
eam (
fi
l
e)
;
f
out
.
w ri
t
e(
dat
as)
;
f
out
.
cl
ose(
)
;
Toast
.
m akeText
(thi
s,"Ecri
ture ef f
ectuée",Toast.
LEN G TH _SH O RT).
show ();
} catch(Excepti
on e) {
Log.
e(
"Fi
chi
erI
nt
er
neAct
i
vi
t
y","Er
r
eursurfi
chi
er",e)
;
}
}
46. Exemple d'écriture depuis SD (1)
publ
i
c voi
d l
i
reFi
chi
erExterne(V i
ew v){
St
ri
ng st
at
e = Envi
r
onm ent
.
get
Ext
er
nal
St
orageSt
at
e(
)
;
i
f(!(state.
equal
s(Envi
ronm ent.
M ED I
A _M O U N TED _R EA D _O N LY) |
|
state.
equal
s(Envi
ronm ent.
M ED I
A _M O U N TED ))){
Log.
e("Fi
chi
erExt
er
neAct
i
vi
t
y","La cart
e SD n'
estpas m ont
ée")
;
Toast
.
m akeText
(
thi
s,"LLa carte SD n'
est pas m ontée",
Toast.
LEN G TH _SH O RT).
show ();
return;
}
try{
Fi
l
e di
r= thi
s.
getExternal
Fi
l
esD i
r(Envi
ronm ent.
D I
R ECTO RY_D O W N LO A D S);
Fi
l
e fi
l
e = new Fi
l
e(
di
r,
"t
est
.
dat
a")
;
Fi
l
eI
nput
St
r
eam fi
n = new Fi
l
eI
nput
St
r
eam (
fi
l
e)
;
i
nt
b;
do{
b = fi
n.
r
ead(
)
;
Log.
i
(
"Fi
chi
erExt
er
neAct
i
vi
t
y","O ct
etl
u :"+ b)
;
} w hi
l
e(b != -1);
fi
n.
cl
ose(
)
;
Toast
.
m akeText
(
thi
s,"Lecture ef f
ectuée",Toast.
LEN G TH _SH O RT).
show ();
}catch(Excepti
on e){
Log.
e("Fi
chi
erI
nt
er
neAct
i
vi
t
y","Er
r
eursurfi
chi
er",e)
;
}}
47. Fichiers temporaires
Un emplacement spécifique à chaque application est
réservé pour les fichiers temporaires
en interne pour des fichiers de petite taille
public File getCacheDir()de la classe Context
sur le support SD pour des fichiers de grande taille
public File getExternalCacheDir() de la classe Context
l'application est chargée de supprimer le fichier temporaire
doit être supprimé dès que l'application n'en a plus besoin
les fichiers temporaires sont supprimés si l'application est
désinstallée