J'ai implémenté Content Provider qui utilise SQLiteDatabase comme source de données de sauvegarde.SQLite DB avec conditions de concurrence Content Provider
Une activité d'écriture dans le DB en appelant getContentResolver().applyBatch(operations)
, qui devrait être atomique.
protected void onPause() {
new Thread(){
@Override
public void run() {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
ContentProviderOperation.Builder builder;
for (Tag tag: mTopicAdapter.getTags()) {
builder = ContentProviderOperation.newUpdate(QuizProvider.TAG_URI);
builder.withValue(Tag.Table.SELECTED, tag.getSelectionStatus());
builder.withSelection(Tag.Table._ID + " = " + tag.getId(), null);
ops.add(builder.build());
}
try {
ContentProviderResult[] res = getContentResolver().applyBatch(QuizProvider.AUTHORITY, ops);
Timber.d("Update result: %d", res.length);
getContentResolver().notifyChange(QuizProvider.TAG_URI, null);
getContentResolver().notifyChange(QuizProvider.QUESTION_URI, null);
} catch (RemoteException e) {
e.printStackTrace();
} catch (OperationApplicationException e) {
e.printStackTrace();
}
}
}.start();
super.onPause();
}
activité deuxième lecture de la DB à l'aide du curseur Loader et obtient parfois des données anciennes (condition de course).
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
Uri randQuestionUri = QuizProvider.QUESTION_URI
.buildUpon()
.appendPath("rand").appendPath(Integer.toString(QUIZ_SIZE))
.build();
return new CursorLoader(this, randQuestionUri, null, null, null, null);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (DEBUG) Timber.d("load finished: %d", data.hashCode());
mPagerAdapter.swapCursor(data);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
mPagerAdapter.swapCursor(null);
}
Projet complet here.
Log sortie:
NSA:QuizProvider:138: update db: selected=false _id = 4
NSA:QuizProvider:138: update db: selected=false _id = 5
NSA:QuizProvider:138: update db: selected=false _id = 26
NSA:QuizProvider:138: update db: selected=false _id = 19
NSA:QuizProvider:138: update db: selected=false _id = 28
NSA:QuizProvider:138: update db: selected=false _id = 10
NSA:QuizProvider:138: update db: selected=false _id = 12
NSA:QuizProvider:138: update db: selected=false _id = 15
NSA:QuizProvider:138: update db: selected=false _id = 18
NSA:QuizProvider:138: update db: selected=false _id = 25
NSA:QuizProvider:138: update db: selected=false _id = 16
NSA:QuizProvider:138: update db: selected=false _id = 17
NSA:QuizProvider:138: update db: selected=false _id = 8
NSA:QuizProvider:138: update db: selected=false _id = 3
NSA:QuizProvider:138: update db: selected=false _id = 20
NSA:QuizProvider:138: update db: selected=false _id = 29
NSA:QuizProvider:138: update db: selected=false _id = 24
NSA:QuizProvider:138: update db: selected=false _id = 23
NSA:QuizProvider:138: update db: selected=false _id = 30
NSA:QuestionsPagerAdapter:42: counter: 0
NSA:QuizProvider:103: query db: content://doit.study.droi question/ran 280 null null
NSA:QuizProvider:138: update db: selected=false _id = 6
NSA:QuestionsPagerAdapter:42: counter: 0
NSA:QuestionsPagerAdapter:42: counter: 0
NSA:QuizProvider:138: update db: selected=false _id = 1
NSA:QuizProvider:138: update db: selected=false _id = 14
NSA:QuestionsPagerAdapter:42: counter: 0
NSA:QuizProvider:138: update db: selected=false _id = 7
NSA:QuizProvider:138: update db: selected=false _id = 27
NSA:QuestionsPagerAdapter:42: counter: 0
NSA:QuestionsActivity:84: load finished: 154982045
NSA:QuestionsPagerAdapter:66: swap cursor, cnt: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:35: instantiateItem, pos=0
NSA:QuestionsPagerAdapter:25: getItem, pos=0
NSA:QuestionsPagerAdapter:35: instantiateItem, pos=1
NSA:QuestionsPagerAdapter:25: getItem, pos=1
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuizProvider:138: update db: selected=false _id = 2
NSA:QuizProvider:138: update db: selected=false _id = 11
NSA:QuizProvider:138: update db: selected=false _id = 22
NSA:QuizProvider:138: update db: selected=false _id = 9
NSA:QuizProvider:138: update db: selected=false _id = 31
NSA:QuizProvider:138: update db: selected=false _id = 21
NSA:QuizProvider:138: update db: selected=false _id = 32
NSA:QuizProvider:138: update db: selected=false _id = 13
NSA:QuizProvider:138: update db: selected=false _id = 4
W/FragmentManager: moveToState: Fragment state for QuestionFragment{5c25e01 #0 id=0x7f0f00e5} not updated inline; expected state 3 found 2
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:52: title pos: 0, questions: tags: [User Interfaces]
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:52: title pos: 1, questions: tags: [User Interfaces]
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuizProvider:103: query db: content://doit.study.droi tag null null
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuestionsPagerAdapter:42: counter: 104
NSA:QuizProvider:138: update db: selected=false _id = 5
NSA:QuizProvider:138: update db: selected=false _id = 26
NSA:QuizProvider:103: query db: content://doit.study.droi tag null null
NSA:QuizProvider:138: update db: selected=false _id = 19
NSA:QuizProvider:138: update db: selected=false _id = 28
NSA:QuizProvider:138: update db: selected=false _id = 10
NSA:QuizProvider:138: update db: selected=false _id = 12
NSA:QuizProvider:138: update db: selected=false _id = 15
NSA:QuizProvider:138: update db: selected=false _id = 18
NSA:QuizProvider:138: update db: selected=false _id = 25
NSA:QuizProvider:138: update db: selected=false _id = 16
NSA:QuizProvider:138: update db: selected=false _id = 17
NSA:QuizProvider:138: update db: selected=false _id = 8
NSA:QuizProvider:138: update db: selected=false _id = 3
NSA:QuizProvider:138: update db: selected=false _id = 20
NSA:QuizProvider:138: update db: selected=false _id = 29
NSA:QuizProvider:138: update db: selected=false _id = 24
NSA:QuizProvider:138: update db: selected=false _id = 23
NSA:QuizProvider:138: update db: selected=false _id = 30
NSA:QuizProvider:138: update db: selected=false _id = 6
NSA:QuizProvider:138: update db: selected=false _id = 1
NSA:QuizProvider:138: update db: selected=false _id = 14
NSA:QuizProvider:138: update db: selected=false _id = 7
NSA:QuizProvider:138: update db: selected=false _id = 27
NSA:QuizProvider:138: update db: selected=false _id = 2
NSA:QuizProvider:138: update db: selected=false _id = 11
NSA:QuizProvider:138: update db: selected=false _id = 22
NSA:QuizProvider:138: update db: selected=false _id = 9
NSA:QuizProvider:138: update db: selected=false _id = 31
NSA:QuizProvider:138: update db: selected=false _id = 21
NSA:QuizProvider:138: update db: selected=false _id = 32
NSA:QuizProvider:138: update db: selected=false _id = 13
NSA:QuizProvider:103: query db: content://doit.study.droi question/ran 280 null null
NSA:QuestionsActivity:84: load finished: 19434496
NSA:QuestionsPagerAdapter:66: swap cursor, cnt: 0
NSA:QuestionsPagerAdapter:42: counter: 0
NSA:QuestionsPagerAdapter:42: counter: 0
Log montre que applyBatch est pas terminé et le curseur Loader obtient partiellement les données modifiées (curseur compteur = 104, doit être de 0 ou 280).
Certaines ressources (désolé, ne peut pas ajouter plus de deux liens):
_http: //developer.android.com/guide/topics/providers/content-provider-basics.html#Batch
_http: //www.androiddesignpatterns.com/2012/10/sqlite-contentprovider-thread-safety.html
_http: //stackoverflow.com/questions/8104832/sqlite-simultaneous-reading-and-writing
_http: //www.grokkingandroid.com/better-performance-with-contentproviderope ration/
Avez-vous une idée de ce qui ne va pas?
Votre code rencontre deux problèmes: la «condition de concurrence» à laquelle vous êtes confronté est causée par l'utilisation d'un nouveau thread chaque fois que vous soumettez une modification au lieu d'utiliser une file d'attente (par exemple AsyncTask avec Executor par défaut). Votre code n'appelle pas 'ContentResolver # notifyChange', donc le CursorLoader n'est pas rechargé quand un changement survient. – user1643723
Merci de votre réponse. Je soumets des modifications une fois en quittant l'activité One dans la méthode onPause(). Ajouté notifyChange() (sur votre conseil). Encore ne peut pas obtenir la signification atomique pour applyBatch (opérations). Comme la sortie du journal montre que ce n'est pas atomique. –
Vous devez appeler 'notifyChange' dans les méthodes de ContentProvider. Vous êtes également censé définir Uri observable sur SQLiteCursor créé en même temps.Apprendre la bonne façon de faire les choses en étudiant le code des ContentProviders existants (je recommande de regarder le code, généré par [AnnotatedSQL] (https://github.com/hamsterksu/Android-AnnotatedSQL/)). Et assurez-vous d'utiliser * AsyncTask au lieu de 'new Thread' *. – user1643723