Professional Documents
Culture Documents
Bases de donnes
et fournisseurs de contenu
Au sommaire de ce chapitre:
Utiliser les fournisseurs de contenu, les curseurs et les content values pour
stocker, partager et consommer des donnes dapplication
03/08/12 07:26
264
Android 4
Les fournisseurs de contenu pouvant tre utiliss entre les applications, vous avez
la possibilit dintgrer plusieurs fournisseurs natifs dans vos propres applications,
comme les contacts, lagenda et le MediaStore. Vous apprendrez stocker et
rcuprer des donnes de ces applications essentielles dAndroid afin de fournir
vos utilisateurs un plus grand confort et une intgration totale avec le systme.
03/08/12 07:26
Chapitre 8
265
Plusieurs fournisseurs de contenu natifs sont accessibles par les applications tierces,
notamment le gestionnaire des contacts, la base de mdias et lagenda, comme nous
le verrons plus loin dans ce chapitre.
En publiant vos propres fournisseurs de contenu, vous vous donnez la possibilit
(ainsi qu dautres dveloppeurs) dincorporer et dtendre vos donnes dans de
nouvelles applications.
Introduction SQLite
SQLite est un systme de gestion de bases de donnes relationnelles (SGBDR) bien
connu. Il est:
open-source;
lger;
mono tiers.
03/08/12 07:26
266
Android 4
Sous Android, le rsultat des requtes est renvoy sous la forme dobjets Cursor.
Ceux-ci sont des pointeurs vers les rsultats et non des extractions des valeurs. Ils
fournissent un moyen de contrler votre position (ligne) dans le rsultat dune requte,
do leur nom..
La classe Cursor inclut de nombreuses fonctions de dplacement dont quelques
exemples suivent:
moveToFirst.
moveToNext.
moveToPrevious.
getCount.
getColumnNames.
getPosition.
Android fournit un mcanisme pratique pour garantir que les requtes seffectuent
de faon asynchrone: la classe CursorLoader et son gestionnaire associ ont t
introduits par Android 3.0 (API level 11) et font dsormais partie de la bibliothque
support, ce qui vous permet den tirer profit tout en supportant les versions plus
anciennes dAndroid.
Plus loin dans ce chapitre, vous apprendrez comment interroger une base de donnes
et extraire des valeurs spcifiques de ligne et de colonne des curseurs rsultants.
03/08/12 07:26
Chapitre 8
267
Introduction SQLiteOpenHelper
est une classe abstraite utilise pour implmenter un modle de
bonnes pratiques pour la cration, louverture et la mise jour des bases de donnes.
SQLiteOpenHelper
03/08/12 07:26
268
Android 4
Info
Dans cet exemple, onUpgrade supprime simplement la table existante et la remplace
par sa nouvelle dfinition. Cest souvent la solution la plus simple et la plus pratique.
Cependant, pour les donnes importantes qui ne sont pas synchronises avec des services
en ligne ou qui sont difficiles reproduire, une meilleure approche consiste migrer
les donnes existantes dans la nouvelle table.
Pour accder une base de donnes via cette classe utilitaire, appelez
getWritableDatabase ou getReadableDatabase pour ouvrir et renvoyer, respectivement, une instance en criture ou en lecture de la base sous-jacente.
03/08/12 07:26
Chapitre 8
269
En coulisses, si la base nexiste pas, lobjet SQLiteOpenHelper excute son gestionnaire onCreate. Si la version de la base a chang, le gestionnaire onUpgrade sera
lanc. Dans les deux cas, lappel getWritableDatabase ou getReadableDatabase
renverra la base de donnes en cache, nouvellement ouverte, nouvellement cre
ou mise jour.
Lorsquune base de donnes a t ouverte, le SQLiteOpenHelper met en cache la
base de donnes qui vient dtre ouverte: vous pouvez (et devriez) donc utiliser ces
mthodes pour chaque interrogation ou transaction sur la base, au lieu de mettre en
cache la base de donnes dans votre application.
Un appel getWritableDatabase peut chouer en raison dun problme despace
disque ou de permissions. Il est donc conseill de se replier sur la mthode
getReadableDatabase pour les requtes. Dans la plupart des cas, cette mthode
renverra la mme instance de base de donnes en cache et ouverte en criture que
getWritableDatabase, sauf si la base nexiste pas encore ou que les mmes problmes
de permission ou despace disque interviennent, auquel cas elle renverra une copie
en lecture seule.
Info
Pour crer ou mettre jour une base de donnes, celle-ci doit tre ouverte en criture.
Il est donc gnralement conseill de tenter dabord de louvrir en criture et de ne
se replier vers une ouverture en lecture seule que si cette premire tentative choue.
Aprs avoir cr la base de donnes, vous devez grer la cration et les mises jour
de versions qui sont normalement prises en charge dans les gestionnaires onCreate
et onUpdate de SQLiteOpenHelper gnralement en utilisant la mthode execSQL
de la base de donnes pour crer et supprimer les tables.
Il est conseill de reporter la cration et louverture des bases de donnes tant quelles
ne sont pas ncessaires et de mettre en cache les instances de la base aprs leur
ouverture afin de limiter les cots que ces oprations induisent en termes defficacit.
Au minimum, ces oprations doivent tre traites de faon asynchrone pour viter
de perturber le thread principal de lapplication.
03/08/12 07:26
270
Android 4
Le Listing8.3 montre comment rcuprer une slection de lignes dune table SQLite.
Listing8.3: Interrogation dune base de donnes
// Prcise la projection des colonnes du rsultat. On renvoie lensemble
// de colonnes minimum correspondant nos besoins.
String[] result_columns = new String[] {
KEY_ID, KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, KEY_GOLD_HOARDED_COLUMN };
03/08/12 07:26
Chapitre 8
271
Info
Dans le Listing 8.3, on ouvre une instance de base de donnes SQLite laide de limplmentation de SQLiteOpenHelper, qui reporte la cration et louverture des instances de
base tant quelles ne sont pas ncessaires et les met en cache aprs leur ouverture.
En consquence, il est conseill de demander une instance de base de donnes chaque
fois que lon effectue une requte ou une transaction sur la base. Pour des raisons
defficacit, vous ne devriez fermer votre instance de base de donnes que si vous
pensez que vous nen aurez plus besoin typiquement, lorsque lactivit ou le service
qui lutilise est stopp.
Info
Les implmentations de bases de donnes doivent publier des constantes statiques qui
donnent les noms des colonnes. Ces constantes statiques sont gnralement exposes
par la classe utilitaire ou le fournisseur de contenu.
03/08/12 07:26
272
Android 4
Les colonnes des bases SQLite tant faiblement types, vous pouvez les transtyper en
types valides selon vos besoins. Les valeurs stockes en virgules flottantes peuvent,
par exemple, tre lues comme des chanes.
Lorsque vous avez fini dutiliser le curseur, il est important de le fermer pour viter
les fuites mmoire et pour rduire les ressources utilises par lapplication:
cursor.close();
03/08/12 07:26
Chapitre 8
273
Insrez la nouvelle ligne en passant cet objet la mthode insert de la base cible,
ainsi que le nom de la table (voir le Listing8.5).
Listing8.5: Insertion de nouvelles lignes dans une base
// Cre une nouvelle ligne insrer.
ContentValues newValues = new ContentValues();
// Affecte des valeurs chaque ligne.
newValues.put(KEY_GOLD_HOARD_NAME_COLUMN, hoardName);
newValues.put(KEY_GOLD_HOARDED_COLUMN, hoardValue);
newValues.put(KEY_GOLD_HOARD_ACCESSIBLE_COLUMN, hoardAccessible);
// [ ... Rpter pour chaque paire nom/valeur de colonne ... ]
// Insre la ligne.
SQLiteDatabase db = hoardDBOpenHelper.getWritableDatabase();
db.insert(HoardDBOpenHelper.DATABASE_TABLE, null, newValues);
Info
Le second paramtre pass insert dans le Listing8.5 est appel "astuce de la colonne
null".
Si vous voulez ajouter une ligne vide dans une base de donnes SQLite en passant un
objet ContentValues vide, vous devez galement passer le nom dune colonne dont la
valeur est explicitement fixe null.
Lorsque vous insrez une nouvelle ligne dans une base de donnes SQLite, vous devez
toujours prciser au moins une colonne et sa valeur correspondante qui peut tre
null. Si le second paramtre de insert est null, comme avec lastuce de la colonne null,
linsertion dun objet ContentValues vide lvera une exception.
Il est gnralement prfrable de sassurer que votre code ne tente pas dinsrer des
ContentValues vides dans une base de donnes SQLite.
03/08/12 07:26
274
Android 4
Pour supprimer une ligne, appelez simplement delete sur la base en indiquant le
nom de la table et une clause where ramenant les lignes que vous voulez supprimer
(voir le Listing8.7).
Listing8.7: Suppression dune ligne
// Crer une clause where prcisant la ou les lignes supprimer.
// Dfinit les paramtres ventuels de where.
String where = KEY_GOLD_HOARDED_COLUMN + =0;
String whereArgs[] = null;
// Supprime les lignes correspondant la clause where.
SQLiteDatabase db = hoardDBOpenHelper.getWritableDatabase();
db.delete(HoardDBOpenHelper.DATABASE_TABLE, where, whereArgs);
Comme pour la classe utilitaire dcrite dans la section prcdente, il est conseill de
dfinir des constantes statiques dans cette classe notamment les noms des colonnes
et lautorit du fournisseur de contenu qui seront requises par la suite pour effectuer
des transactions sur la base de donnes ou pour linterroger.
Vous devez galement redfinir la mthode onCreate pour crer (et initialiser) la
source de donnes sous-jacente, ainsi que les mthodes query, update, delete, insert
et getType pour implmenter linterface utilise par le rsolveur de contenus pour
interagir avec les donnes, comme nous lexpliquons dans les sections qui suivent.
03/08/12 07:26
Chapitre 8
275
Ces URI de contenu seront utilises par un rsolveur de contenus pour accder
votre fournisseur. Une requte utilisant lURI ci-dessus reprsente une requte de
toutes les lignes, alors quune URI se terminant par /<numro de ligne>, comme
dans lexemple ci-dessous, permet de crer une requte dune seule ligne:
content://com.paad.skeletondatabaseprovider/elements/5
Il est conseill de supporter ces deux formes daccs votre fournisseur. Le moyen
le plus simple dy parvenir consiste utiliser un UriMatcher pour analyser les URI
et dterminer leurs formes.
Le Listing 8.8 montre un squelette dimplmentation pour dfinir un analyseur dURI
qui dtermine si une URI est une requte de toutes les donnes ou dune simple ligne.
Listing8.8: Dfinition dun UriMatcher pour dterminer si une requte porte sur
tous les lments ou sur une seule ligne
// Cre les constantes utilises pour diffrencier les requtes
private static final int ALLROWS = 1;
private static final int SINGLE_ROW = 2;
03/08/12 07:26
276
Android 4
Vous pouvez utiliser la mme technique pour exposer des URI alternatives pour diffrents sous-ensembles de donnes ou diffrentes tables dans votre base, en utilisant
le mme fournisseur de contenu.
Maintenant que vous savez diffrencier les requtes sur toute une table et sur une seule
ligne, vous pouvez vous servir de la classe SQLiteQueryBuilder pour appliquer une
condition de slection supplmentaire votre requte, comme dans lexemple suivant:
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// Si cest une requte dune seule ligne, on limite lensemble rsultat
// la ligne transmise en paramtre.
switch (uriMatcher.match(uri)) {
case SINGLE_ROW :
String rowID = uri.getPathSegments().get(1);
queryBuilder.appendWhere(KEY_ID + = + rowID);
default: break;
}
Plus loin dans ce chapitre, nous verrons comment effectuer une requte laide de
SQLiteQueryBuilder .
Crer la base de donnes du fournisseur de contenu
Pour initialiser la source de donnes laquelle vous comptez accder au moyen
du fournisseur de contenu, redfinissez la mthode onCreate comme dans le Listing8.9. On utilise gnralement une implmentation de SQLiteOpenHelper, du type
dcrit dans la section prcdente, afin de reporter la cration et louverture de la
base de donnes tant quelle nest pas ncessaire.
Listing 8.9: Cration de la base de donnes du fournisseur de contenu
private MySQLiteOpenHelper myOpenHelper;
@Override
public boolean onCreate() {
// Construction de la base de donnes sous-jacente.
// Reporte louverture de la base tant que lon nen a pas besoin
// pour une requte ou une transaction.
myOpenHelper = new MySQLiteOpenHelper(getContext(),
03/08/12 07:26
Chapitre 8
277
MySQLiteOpenHelper.DATABASE_NAME, null,
MySQLiteOpenHelper.DATABASE_VERSION);
return true;
}
Info
Lorsque votre application est lance, le gestionnaire onCreate de chacun de ses fournisseurs de contenu est excut dans le thread principal de lapplication.
Comme pour les exemples prcdents de la section prcdente, la meilleure approche
consiste utiliser un SQLiteOpenHelper pour reporter louverture (et, si ncessaire, la
cration) de la base de donnes sous-jacente tant quelle nest pas ncessaire dans les
mthodes query et transaction du fournisseur de contenu.
Pour des raisons defficacit, il est prfrable de laisser le fournisseur de contenu ouvert
tant que lapplication sexcute; il nest pas utile de fermer manuellement la base de
donnes. Si le systme a besoin de ressources, votre application sera tue et les bases
de donnes associes seront fermes.
03/08/12 07:26
278
Android 4
db = myOpenHelper.getReadableDatabase();
}
// remplacer par des instructions SQL valides si ncessaire.
String groupBy = null;
String having = null;
// Utilisation dun objet SQLiteQueryBuilder pour simplifier la
// construction de la requte.
SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
// Si cest une requte de ligne, on limite lensemble rsultat
// la ligne passe en paramtre.
switch (uriMatcher.match(uri)) {
case SINGLE_ROW :
String rowID = uri.getPathSegments().get(1);
queryBuilder.appendWhere(KEY_ID + = + rowID);
default: break;
}
// Prcise la table sur laquelle effectuer la requte.
// Il peut sagir dune table spcifique ou dune jointure.
queryBuilder.setTables(MySQLiteOpenHelper.DATABASE_TABLE);
// Excute la requte.
Cursor cursor = queryBuilder.query(db, projection, selection,
selectionArgs, groupBy, having,
sortOrder);
// Renvoie le curseur rsultat.
return cursor;
}
Si vous implmentez les requtes, vous devez galement prciser un type MIME
pour identifier les donnes renvoyes. Pour cela, redfinissez la mthode getType
pour quelle renvoie une chane dcrivant de faon unique le type de vos donnes.
Le type renvoy pourra tre de deux formes une pour les entres simples, lautre
pour toutes les entres:
lment simple:
vnd.android.cursor.item/vnd.<nomSocit>.<typeContenu>
Le Listing 8.11 montre comment redfinir la mthode getType pour quelle renvoie
le type MIME correct en fonction de lURI.
Listing 8.11: Renvoi du type MIME dun fournisseur de contenu
@Override
public String getType(Uri uri) {
03/08/12 07:26
Chapitre 8
279
Pour exposer les transactions de suppression, insertion et mise jour sur votre
fournisseur de contenu, implmentez les mthodes delete, insert, et update
correspondantes.
Comme la mthode query, ces mthodes seront utilises par le rsolveur de contenus
pour effectuer les transactions sur les donnes sous-jacentes sans connatre leur
implmentation ce qui permet aux applications de modifier des donnes entre
elles. Il est prfrable dutiliser la mthode notifyChange du rsolveur lorsque vous
effectuez des transactions qui modifient les donnes des tables. Celle-ci prviendra
les observateurs de contenu enregistrs pour un curseur donn ( laide de la mthode
Cursor.registerContentObserver) que la table sous-jacente (ou lune de ses lignes)
a t supprime, ajoute ou modifie. Comme pour les requtes, le cas dutilisation
le plus frquent est lexcution dune transaction sur une base de donnes SQLite,
bien que ce ne soit pas obligatoire. Le Listing8.12 est un squelette de code qui implmente des transactions sur un fournisseur de contenu utilisant une base de donnes
SQLite sous-jacente.
Listing8.12: Implmentation typique des transactions sur un fournisseur de contenu
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Ouvre la base en lecture/criture pour la transaction.
SQLiteDatabase db = myOpenHelper.getWritableDatabase();
// Si cest une URI de ligne, limite la suppression la ligne indique.
switch (uriMatcher.match(uri)) {
case SINGLE_ROW :
String rowID = uri.getPathSegments().get(1);
selection = KEY_ID + = + rowID
+ (!TextUtils.isEmpty(selection) ?
AND ( + selection + ) : );
default: break;
}
//
//
//
if
03/08/12 07:26
280
Android 4
// Effectue la suppression.
int deleteCount = db.delete(MySQLiteOpenHelper.DATABASE_TABLE,
selection, selectionArgs);
// Prvient les observateurs que lensemble des donnes a t modifi.
getContext().getContentResolver().notifyChange(uri, null);
// Renvoie le nombre dlments supprims.
return deleteCount;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// Ouvre la base en lecture/criture pour la transaction.
SQLiteDatabase db = myOpenHelper.getWritableDatabase();
// Pour ajouter des lignes vides la base en passant un objet
// ContentValues vide, vous devez utiliser lastuce du paramtre de
// colonne null pour indiquer le nom de la colonne qui peut tre mise
// null.
String nullColumnHack = null;
// Insre les valeurs dans la table.
long id = db.insert(MySQLiteOpenHelper.DATABASE_TABLE,
nullColumnHack, values);
// Construit et renvoie lURI de la ligne insre.
if (id > -1) {
// Construit et renvoie lURI de la ligne insre.
Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);
// Prvient les observateurs que lensemble des donnes a t modifi.
getContext().getContentResolver().notifyChange(insertedId, null);
return insertedId;
} else
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// Ouvre la base en lecture/criture pour la transaction.
SQLiteDatabase db = myOpenHelper.getWritableDatabase();
// Si cest une URI de ligne, limite la modification la ligne indique.
switch (uriMatcher.match(uri)) {
case SINGLE_ROW :
String rowID = uri.getPathSegments().get(1);
selection = KEY_ID + = + rowID
+ (!TextUtils.isEmpty(selection) ?
AND ( + selection + ) : );
default: break;
}
// Effectue la modification.
03/08/12 07:26
Chapitre 8
281
Info
La classe ContentUris fournit la mthode withAppendedId qui permet dajouter facilement
un identifiant de ligne la valeur CONTENT_URI dun fournisseur de contenu. Nous lutilisons dans le Listing8.12 pour construire lURI des lignes que lon vient dinsrer et
nous nous en servirons galement dans les sections suivantes pour dsigner une ligne
particulire au cours des requtes et des transactions sur la base de donnes.
03/08/12 07:26
282
Android 4
Info
Les fichiers associs des lignes dans la base de donnes tant stocks lextrieur
de celle-ci, il est important de rflchir leffet que devrait avoir la suppression dune
ligne sur le fichier sous-jacent.
android.content.ContentProvider;
android.content.ContentUris;
android.content.ContentValues;
android.content.Context;
android.content.UriMatcher;
android.database.Cursor;
android.database.sqlite.SQLiteDatabase;
android.database.sqlite.SQLiteDatabase.CursorFactory;
android.database.sqlite.SQLiteOpenHelper;
android.database.sqlite.SQLiteQueryBuilder;
android.net.Uri;
android.text.TextUtils;
android.util.Log;
03/08/12 07:26
Chapitre 8
283
03/08/12 07:26
284
Android 4
// Effectue la suppression.
int deleteCount = db.delete(MySQLiteOpenHelper.DATABASE_TABLE,
selection, selectionArgs);
// Prvient les observateurs que lensemble des donnes a t modifi.
getContext().getContentResolver().notifyChange(uri, null);
// Renvoie le nombre dlments supprims.
return deleteCount;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// Ouvre la base en lecture/criture pour la transaction.
SQLiteDatabase db = myOpenHelper.getWritableDatabase();
// Pour ajouter des lignes vides la base en passant un objet
// ContentValues vide, vous devez utiliser lastuce du paramtre de
03/08/12 07:26
Chapitre 8
285
// colonne null pour indiquer le nom de la colonne qui peut tre mise
// null.
String nullColumnHack = null;
// Insre les valeurs dans la table.
long id = db.insert(MySQLiteOpenHelper.DATABASE_TABLE,
nullColumnHack, values);
// Construit et renvoie lURI de la ligne insre.
if (id > -1) {
// Construit et renvoie lURI de la ligne insre.
Uri insertedId = ContentUris.withAppendedId(CONTENT_URI, id);
// Prvient les observateurs que lensemble des donnes a t modifi.
getContext().getContentResolver().notifyChange(insertedId, null);
return insertedId;
} else
return null;
}
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// Ouvre la base en lecture/criture pour la transaction.
SQLiteDatabase db = myOpenHelper.getWritableDatabase();
// Si cest une URI de ligne, limite la modification la ligne indique.
switch (uriMatcher.match(uri)) {
case SINGLE_ROW :
String rowID = uri.getPathSegments().get(1);
selection = KEY_ID + = + rowID
+ (!TextUtils.isEmpty(selection) ?
AND ( + selection + ) : );
default: break;
}
// Effectue la modification.
int updateCount = db.update(MySQLiteOpenHelper.DATABASE_TABLE,
values, selection, selectionArgs);
// Prvient les observateurs que lensemble des donnes a t modifi.
getContext().getContentResolver().notifyChange(uri, null);
return updateCount;
}
@Override
public String getType(Uri uri) {
// Renvoie une chane qui identifie le type MIME
// dune URI de fournisseur de contenu.
switch (uriMatcher.match(uri)) {
case ALLROWS:
return vnd.android.cursor.dir/vnd.paad.elemental;
case SINGLE_ROW:
return vnd.android.cursor.item/vnd.paad.elemental;
default:
03/08/12 07:26
286
Android 4
Lorsque les fournisseurs de contenu sont utiliss pour exposer des donnes, les
rsolveurs de contenu sont les classes correspondantes permettant dinterroger et
deffectuer des transactions sur ces fournisseurs. Tandis que les fournisseurs de
contenu offrent une abstraction par rapport aux donnes sous-jacentes, les rsolveurs
de contenu fournissent une abstraction par rapport au fournisseur qui est interrog
ou manipul.
Le rsolveur de contenu inclut les mthodes pour les requtes et les transactions
correspondant celles qui ont t dfinies dans vos fournisseurs. Il na pas besoin
de connatre limplmentation des fournisseurs de contenu avec lesquels il interagit
chaque mthode de requte ou transaction prend simplement en paramtre une
URI qui indique le fournisseur de contenu concern.
Une URI de fournisseur de contenu est son autorit dfinie dans son manifeste et
gnralement publie sous la forme dune constante statique de limplmentation du
fournisseur.
Les fournisseurs de contenu acceptent en gnral deux formes dURI, lune pour les
requtes sur toutes les donnes et lautre pour les requtes sur une seule ligne. Dans
cette dernire, un /<rowID> est ajout lURI de base.
Effectuer des requtes
Les requtes sur un fournisseur de contenu sont trs semblables celles effectues
sur une base de donnes. Les rsultats sont renvoys sous forme de curseurs de la
faon dcrite plus haut dans ce chapitre.
2012 Pearson France Android 4 Reto Meier
03/08/12 07:26
Chapitre 8
287
Vous pouvez extraire les valeurs dun curseur en utilisant les mmes techniques que
celles dcrites dans la section "Extraire les rsultats dun curseur".
Utilisez la mthode query sur lobjet ContentResolver en lui passant les lments
suivants:
Une projection numrant les colonnes que vous voulez inclure dans le rsultat.
Une clause where dfinissant les lignes ramener. Vous pouvez inclure des
jokers ? qui seront remplacs par les valeurs passes par le paramtre des
arguments de slection.
Dans cet exemple, la requte utilise les constantes statiques fournies par la classe
MyHoardContentProvider, mais une application tierce aurait trs bien pu excuter la
mme requte pourvu quelle connaisse lURI du contenu et les noms des colonnes,
et quelle dispose des permissions appropries.
03/08/12 07:26
288
Android 4
Pour extraire les valeurs dun curseur, utilisez les mmes techniques que celles que
nous avons dcrites plus haut, en vous servant des mthodes moveTo<endroit> et
get<type> pour extraire les valeurs de la ligne et de la colonne.
Le Listing8.17 tend le code du Listing8.16 en parcourant un curseur pour afficher
le nom du plus gros magot.
Listing 8.17: Extraction les valeurs du curseur dun fournisseur de contenu
loat largestHoard = 0f;
String hoardName = Pas de magot;
// Trouve les indices des colonnes utilises.
int GOLD_HOARDED_COLUMN_INDEX = resultCursor.getColumnIndexOrThrow(
MyHoardContentProvider.KEY_GOLD_HOARDED_COLUMN);
int HOARD_NAME_COLUMN_INDEX = resultCursor.getColumnIndexOrThrow(
MyHoardContentProvider.KEY_GOLD_HOARD_NAME_COLUMN);
// Parcourt les lignes du curseur.
// Le curseur est plac avant la premire ligne lorsquil est initialis.
03/08/12 07:26
Chapitre 8
289
Lorsque lon a termin dutiliser le curseur, il est important de le fermer pour viter
les fuites mmoire et pour rduire les ressources consommes par lapplication.
Vous verrez dautres exemples plus loin dans ce chapitre lorsque nous prsenterons
les fournisseurs de contenu natifs Android.
Attention
Les requtes sur les bases de donnes peuvent durer un certain temps. Par dfaut, le
rsolveur de contenu excutera les requtes et les autres transactions dans le thread
principal de lapplication.
Pour garantir que votre application restera ractive, vous devez excuter toutes les
requtes de faon asynchrone, comme nous lexpliquons dans la section qui suit.
Les chargeurs sont disponibles pour chaque activit et fragment via la classe
LoaderManager. Ils sont conus pour charger les donnes de faon asynchrone et
pour surveiller les modifications de la source de donnes sous-jacente.
Bien que les chargeurs puissent tre implments pour charger nimporte quelle sorte
de donnes partir de nimporte quelle source, la classe CursorLoader mrite une
attention spciale. Un chargeur de curseur permet en effet deffectuer des requtes
2012 Pearson France Android 4 Reto Meier
03/08/12 07:26
290
Android 4
Un chargeur de curseur gre toutes les tches ncessaires lutilisation dun curseur
dans une activit ou un fragment, ce qui rend obsoltes les mthodes managedQuery
et startManagingCursor dActivity.
Ces tches incluent notamment la gestion du cycle de vie des curseurs pour garantir
quils seront ferms lorsque lactivit sera termine.
Les chargeurs de curseurs surveillent galement les modifications du contenu sousjacent: vous navez donc plus besoin dimplmenter vos propres observateurs de
contenu.
Implmenter les fonctions de rappel dun chargeur de curseur
Si vous navez besoin que dune seule implmentation de chargeur dans votre activit
ou votre fragment, la dmarche classique consiste implmenter cette interface dans
ce composant.
Les fonctions de rappel dun chargeur consistent en trois gestionnaires:
OnCreateLoader.
OnLoadFinished.
03/08/12 07:26
Chapitre 8
291
OnLoaderReset.
Attention
onLoadFinished et onLoaderReset ne sont pas synchronises avec le thread de linterface
utilisateur. Si vous voulez modifier directement les lments de linterface, vous devrez
dabord vous synchroniser avec le thread de linterface utilisateur laide dun gestionnaire ou dun mcanisme similaire. La synchronisation avec le thread de linterface
utilisateur sera tudie en dtail au Chapitre9.
03/08/12 07:26
292
Android 4
Ceci est gnralement ncessaire lorsque les paramtres de la requte changent dans
le cas de recherches ou de modifications de lordre du tri, notamment.
Ajouter, mettre jour et supprimer du contenu
Pour effectuer des transactions sur des fournisseurs de contenu, utilisez les mthodes
delete, update et insert sur lobjet ContentResolver. Comme les requtes, sauf si
elles sont dplaces dans un thread de travail, les transactions sur un fournisseur de
contenu sexcuteront dans le thread principal de lapplication.
Info
Les oprations sur les bases de donnes pouvant durer un certain temps, il est important
dexcuter chaque transaction de faon asynchrone.
Insertions
Le rsolveur de contenu propose deux mthodes pour insrer de nouveaux enregistrements dans un fournisseur de contenu, insert et bulkInsert. Toutes les deux prennent
en paramtre lURI du fournisseur de contenu dans lequel vous insrez llment. La
premire prend un objet ContentValues en entre et la seconde, un tableau.
La mthode insert renvoie une URI vers lenregistrement insr alors que bulkInsert
renvoie le nombre de lignes ajoutes.
2012 Pearson France Android 4 Reto Meier
03/08/12 07:26
Chapitre 8
293
Suppressions
Mises jour
Les mises jour des lignes dun fournisseur de contenu sont effectues par la mthode
update du rsolveur de contenu. Cette mthode reoit lURI du fournisseur de contenu
cible, un objet ContentValues contenant les valeurs des colonnes mettre jour et
une clause where qui indique quelles lignes mettre jour.
03/08/12 07:26
294
Android 4
Lorsque la mise jour est effectue, chaque ligne concerne par la clause where est
mise jour par les ContentValues, et le nombre de mises jour russies est renvoy.
Vous pouvez aussi choisir de modifier une ligne spcifique en indiquant son URI
unique, comme dans le Listing8.21.
Listing8.21: Mise jour dun enregistrement dans un fournisseur de contenu
// Cre le contenu modifi de la ligne, en affectant des valeurs
// chaque ligne.
ContentValues updatedValues = new ContentValues();
updatedValues.put(MyHoardContentProvider.KEY_GOLD_HOARDED_COLUMN,
newHoardValue);
// [ ... Rpter pour chaque colonne modifier ... ]
// Cre une URI pour dsigner une ligne prcise.
Uri rowURI =
ContentUris.withAppendedId(MyHoardContentProvider.CONTENT_URI,
hoardId);
// On indique une ligne spcifique : il ny a donc pas besoin de clause
// de slection.
String where = null;
String whereArgs[] = null;
// Rcupre le rsolveur de contenu.
ContentResolver cr = getContentResolver();
// Modifie la ligne indique.
int updatedRowCount =
cr.update(rowURI, updatedValues, where, whereArgs);
03/08/12 07:26
Chapitre 8
295
03/08/12 07:26
296
Android 4
android.content.ContentProvider;
android.content.ContentUris;
android.content.ContentValues;
android.content.Context;
android.content.UriMatcher;
android.database.Cursor;
android.database.sqlite.SQLiteDatabase;
android.database.sqlite.SQLiteQueryBuilder;
android.database.sqlite.SQLiteDatabase.CursorFactory;
android.database.sqlite.SQLiteOpenHelper;
android.net.Uri;
android.text.TextUtils;
android.util.Log;
03/08/12 07:26
Chapitre 8
297
3. Crez des variables statiques publiques qui dfinissent les noms des colonnes.
Elles serviront lobjet MySQLiteOpenHelper pour crer la base et aux autres
composants dapplication pour extraire des valeurs de vos requtes.
public static final String KEY_ID = _id;
public static final String KEY_TASK = task;
public static final String KEY_CREATION_DATE = creation_date;
03/08/12 07:26
298
Android 4
03/08/12 07:26
Chapitre 8
299
03/08/12 07:26
300
Android 4
// Excute la suppression.
int deleteCount = db.delete(MySQLiteOpenHelper.DATABASE_TABLE, selection,
selectionArgs);
// Prvient les observateurs de la modification de lensemble des donnes.
getContext().getContentResolver().notifyChange(uri, null);
return deleteCount;
}
@Override
public Uri insert(Uri uri, ContentValues values) {
// Ouvre une base en lecture/criture pour la transaction.
SQLiteDatabase db = myOpenHelper.getWritableDatabase();
// Pour ajouter des lignes vides la base de donnes en passant un
// ContentValues vide, il faut utiliser le paramtre de colonne null pour
// indiquer le nom de la colonne qui peut tre initialise null.
String nullColumnHack = null;
// Insre les valeurs dans la table.
long id = db.insert(MySQLiteOpenHelper.DATABASE_TABLE,
nullColumnHack, values);
03/08/12 07:26
Chapitre 8
301
03/08/12 07:26
302
Android 4
03/08/12 07:26
Chapitre 8
303
Info
Tous les extraits de code de cet exemple font partie du projet Todo List Chapitre 8,
disponible sur le site consacr cet ouvrage.
Vous avez cr une base de donnes dans laquelle sauvegarder vos tches. Une
meilleure approche que la copie des lignes du curseur dans une ArrayList consiste
utiliser un SimpleCursorAdapter. Nous le ferons plus loin dans ce chapitre, dans
la section "Crer un fournisseur de tremblements de terre avec la fonctionnalit de
recherche".
Pour rendre cette application de liste de tches plus utile, vous pourriez ajouter la
possibilit de supprimer, modifier les tches de la liste, modifier lordre du tri et vous
pourriez stocker des informations supplmentaires.
03/08/12 07:26