Was sind die besten Vorgehensweisen für SQLite unter Android?
Was als die besten Praktiken bei der Ausführung von Abfragen auf eine SQLite-Datenbank in einer Android-app?
Ist es sicher zu laufen Einfügungen, Löschungen und wählen Sie Abfragen aus einer AsyncTask ist doInBackground? Oder sollte ich mit dem UI-Thread? Ich nehme an, dass Datenbank-Abfragen können "schwere" und sollte nicht mit dem UI-thread kann lock up der app - was eine Anwendung Reagiert Nicht (ANR).
Wenn ich mehrere AsyncTasks, sollten Sie eine Verbindung gemeinsam nutzen, oder sollten Sie eine Verbindung öffnen, jeder?
Gibt es bewährte Verfahren für diese Szenarios?
Kommentar zu dem Problem
Was auch immer Sie tun, denken Sie daran, desinfizieren Sie Ihre Eingaben, wenn Sie Ihre content-provider (oder SQLite-Schnittstelle) öffentlich vor!
Sie definitiv NICHT tun sollte db-Zugriffe aus dem UI-thread, ich kann Ihnen sagen, dass viel.
@EdwardFalk Warum nicht? Sicherlich gibt es Anwendungsfälle, bei denen es gilt, dies zu tun?
Wenn Sie eine I/O -, Netzwerk-Zugriffe, etc. aus der UI-thread, das ganze Gerät friert ein, bis der Vorgang abgeschlossen ist. Wenn es abgeschlossen ist in 1/20 Sekunde, dann fein. Wenn es länger dauert, du hast eine schlechte user experience.
InformationsquelleAutor der Frage Vidar Vestnes | 2010-03-22
Du musst angemeldet sein, um einen Kommentar abzugeben.
Inserts, updates, deletes und reads sind in der Regel OK, die von mehreren threads, aber Brad Antwort ist nicht korrekt. Sie müssen vorsichtig sein mit, wie man erstellen Sie Ihre verbindungen, und verwenden Sie Sie. Es gibt Situationen, in denen Sie Ihre update-Aufrufe fehl, auch wenn sich Ihre Datenbank nicht beschädigt werden.
Die grundsätzliche Antwort.
Den SqliteOpenHelper-Objekt hält eine Datenbank-Verbindung. Es erscheint zu bieten Ihnen einen Lesen und schreiben-Verbindung, aber es ist wirklich nicht. Rufen Sie die read-only, und Sie erhalten die schreiben der Datenbank-Verbindung unabhängig.
So, ein Helfer Beispiel, eine db-Verbindung. Sogar, wenn Sie die Verwendung von mehreren threads, eine Verbindung gleichzeitig. Die SqliteDatabase-Objekt verwendet die java-locks halten den Zugriff serialisiert. Also, wenn 100 threads haben eine db-Instanz, ruft die eigentliche on-disk-Datenbank serialisiert werden.
So, ein Helfer, eine db-Verbindung, die serialisiert in java-code. Ein thread, der 1000 threads, wenn Sie einen Helfer Instanz zwischen Ihnen geteilt, die alle Ihre db-Zugriffs-code ist die Seriennummer. Und das Leben ist gut (ish).
Wenn Sie versuchen, auf die Datenbank schreiben vom tatsächlichen verschiedene verbindungen zur gleichen Zeit, wird man scheitern. Es wird nicht warten, bis das erste fertig ist und dann schreiben. Es wird einfach nicht schreiben Sie Ihre änderung. Schlimmer noch, wenn Sie rufen Sie nicht die richtige version von insert - /update-auf der SQLiteDatabase, werden Sie nicht bekommen eine Ausnahme. Sie erhalten nur eine Nachricht in deinem LogCat, und das wird es sein.
Also mehrere threads? Verwenden Sie einen Helfer. Zeitraum. Wenn Sie WISSEN, dass nur ein thread zu schreiben, KÖNNEN Sie in der Lage sein, mehrere verbindungen und Ihre liest wird schneller sein, aber Käufer Vorsicht. Habe ich noch nicht getestet viel.
Hier ist ein blog-post mit weit mehr detail und eine Beispiel-app.
Grau und ich sind eigentlich die Nachbereitung ein ORM-tool, basierend auf seiner Ormlite, das funktioniert nativ mit Android-Datenbank-Implementierungen, und folgt den sicheren Aufbau/Aufruf-Struktur, die ich beschreibe in dem blog-post. Das sollte sich sehr bald. Werfen Sie einen Blick.
In der Zwischenzeit gibt es eine follow-up-blog-post:
Auch Kasse die Gabel durch 2point0 der zuvor erwähnten sperren Beispiel:
InformationsquelleAutor der Antwort Kevin Galligan
Gleichzeitigen Datenbank-Zugriff
Gleichen Artikel auf meinem blog(ich mag die Formatierung mehr)
Ich schrieb kleine Artikel, die beschreiben, wie man den Zugriff auf Ihr android-Datenbank thread-safe.
Vorausgesetzt, Sie haben Ihre eigenen SQLiteOpenHelper.
Nun wollen Sie zum schreiben von Daten in Datenbank in separate threads.
Erhalten Sie folgende Meldung in deinem logcat und deine änderungen werden nicht geschrieben.
Dies geschieht, weil jedes mal, wenn Sie erstellen neue SQLiteOpenHelper Objekt, das Sie tatsächlich eine neue Datenbank-Verbindung. Wenn Sie versuchen, auf die Datenbank schreiben vom tatsächlichen verschiedene verbindungen zur gleichen Zeit, wird man scheitern. (Antwort oben)
Verwenden Datenbank mit mehreren threads, die wir brauchen, um sicherzustellen, dass wir mit einer Datenbank-Verbindung.
Let ' s make singleton-Klasse Datenbank-Manager, das halten und die Rückgabe einzelner SQLiteOpenHelper Objekt.
Aktualisierten code, das schreiben von Daten in die Datenbank in separate threads Aussehen wird.
Dadurch wird Sie ein weiteres crash.
Da sind wir mit nur einer Datenbank-Verbindung, Methode getDatabase() Rückkehr derselben Instanz von SQLiteDatabase Objekt für Thread1 und Thread2 verzahnt. Was ist passiert, Thread1 kann die Datenbank schließen, während Thread2 verzahnt ist es immer noch mit. Das ist, warum wir haben IllegalStateException Absturz.
Müssen wir sicherstellen, dass niemand mit Datenbank und nur schließen Sie es dann. Einige Leute auf stackoveflow empfehlenswert, nie schließen Sie Ihre SQLiteDatabase. Es klingt nicht nur dumm, sondern auch Ehre, die Sie mit folgenden logcat Meldung.
Beispiel arbeiten
Verwenden Sie es wie folgt.
Jedes mal müssen Sie die Datenbank, die Sie anrufen sollte openDatabase() Methode der DatabaseManager Klasse. Innerhalb dieser Methode, wir haben einen Zähler, die angeben, wie oft die Datenbank geöffnet wird. Wenn es gleich ein, das heißt, wir müssen erstellen Sie neue Datenbank-Verbindung, wenn nicht, Datenbank-Verbindung ist bereits erstellt.
Das gleiche passiert in closeDatabase() Methode. Jedes mal, wenn wir diese Methode aufrufen, Zähler verringert wird, wenn es auf null geht, sind wir das schließen der Datenbank-Verbindung.
Nun sollten Sie in der Lage sein, um die Verwendung Ihrer Datenbank, und sicher sein, es ist thread-sicher.
InformationsquelleAutor der Antwort Dmytro Danylyk
Thread
oderAsyncTask
für lang laufende Operationen (50ms+). Testen Sie Ihre app, um zu sehen, wo das ist. Die meisten Operationen (wahrscheinlich) nicht erfordern ein thread, da die meisten Operationen (wahrscheinlich), bei denen nur ein paar Zeilen. Verwenden Sie einen thread für bulk-Operationen.SQLiteDatabase
- Instanz für jede DB auf der Festplatte zwischen threads und implementieren ein zählen-system zum nachverfolgen von offenen verbindungen.Teilen sich ein statisches Feld zwischen allen Klassen. Ich verwendet, um ein singleton, um für diese und andere Dinge, die müssen geteilt werden. Ein zählen-System (in der Regel mit AtomicInteger) sollte auch verwendet werden, um sicherzustellen, dass Sie nie schließen Sie die Datenbank, früh-oder lassen Sie es offen.
Die aktuelle version finden Sie unter https://github.com/JakarCo/databasemanager aber ich werde versuchen, halten Sie den code aktuell auch hier. Wenn man verstehen will, meine Lösung ist, schauen Sie auf den code, und Lesen Sie meine Notizen. Meine Noten sind in der Regel sehr hilfreich.
DatabaseManager
. (oder laden Sie es aus dem github)DatabaseManager
und implementierenonCreate
undonUpgrade
wie Sie es normalerweise tun würden. Sie können mehrere Unterklassen der eineDatabaseManager
Klasse, um unterschiedliche Datenbanken auf der Festplatte.getDb()
zu verwenden, dieSQLiteDatabase
Klasse.close()
für jede Unterklasse instanziiertDen code kopieren/einfügen:
InformationsquelleAutor der Antwort Jakar
Die Datenbank ist sehr flexibel mit multi-threading. Meine apps schlagen Ihre DBs aus vielen verschiedenen threads gleichzeitig und es funktioniert Prima. In einigen Fällen habe ich mehrere Prozesse schlagen die DB gleichzeitig und das funktioniert auch.
Ihre asynchrone Aufgaben - dieselbe Verbindung verwenden, wenn Sie können, aber wenn Sie zu haben, seine OK, um den Zugriff auf die DB aus verschiedenen Aufgaben.
InformationsquelleAutor der Antwort Brad Hein
Dmytro Antwort funktioniert gut für meinen Fall.
Ich denke, es ist besser zu erklären, die Funktion als synchronisiert. zumindest für meinen Fall, es würde invoke null-Zeiger-Ausnahme anderweitig, z.B. getWritableDatabase noch nicht wieder in einem thread, und openDatabse genannt in einem anderen thread Zwischenzeit.
InformationsquelleAutor der Antwort gonglong
Mein Verständnis von SQLiteDatabase-APIs ist, dass in den Fall, Sie haben ein multi-threaded-Anwendung, die Sie sich nicht leisten können, mehr als ein 1 SQLiteDatabase-Objekt zeigt auf eine einzelne Datenbank.
Das Objekt definitiv erstellt werden kann, aber die inserts/updates fehlschlagen, wenn verschiedene threads/Prozesse (zu) beginnen mit unterschiedlichen SQLiteDatabase-Objekte (wie wir in JDBC-Verbindung).
Die einzige Lösung hier ist, mit Stock, 1 SQLiteDatabase Objekte und immer, wenn ein startTransaction() verwendet wird, in mehr als 1 thread, Android verwaltet die sperren in verschiedenen threads und erlaubt nur 1 thread zu einem Zeitpunkt auf die ausschließliche update Zugang.
Außerdem kann man "Liest" aus der Datenbank und verwenden die gleichen SQLiteDatabase-Objekt in einem anderen thread (beim anderen thread schreibt) und würde es nie werden-Datenbank Korruption ich.e "thread Lesen" ist das nicht Lesen der Daten aus der Datenbank bis zum "schreib-thread" verpflichtet sich die Daten zwar verwenden beide die gleichen SQLiteDatabase-Objekt.
Dies ist anders aus, wie connection-Objekts in JDBC, wo, wenn Sie vorbeikommen (Verwendung der gleichen) das connection-Objekt, die zwischen lese-und schreib-threads, dann würden wir wahrscheinlich Druck nicht gespeicherte Daten zu.
In meinem Unternehmen Anwendung, versuche ich die Verwendung von bedingten überprüft, sodass der UI-Thread nicht zu warten, während die BG-thread hält das SQLiteDatabase-Objekt (ausschließlich). Ich versuche, vorherzusagen, UI-Aktionen verschieben und BG-thread ausgeführt für 'x' Sekunden. Auch kann man pflegen PriorityQueue zum verwalten der aushändigung SQLiteDatabase-Verbindung Objekte so, dass der UI-Thread bekommt es zuerst.
InformationsquelleAutor der Antwort Swaroop
nach kämpfen mit diesem für ein paar Stunden, die ich gefunden habe, dass Sie nur verwenden Sie eine db-helper-Objekt pro db-Ausführung. Zum Beispiel,
als adaptiert:
erstellen einer neuen DBAdapter jedes mal, wenn die Schleife iteriert war der einzige Weg, ich könnte meine strings in eine Datenbank durch meine helper Klasse.
InformationsquelleAutor der Antwort dell116
Hatte einige Probleme, ich glaube, ich habe verstanden, warum ich gegangen sind falsch.
Ich geschrieben hatte, ein Datenbank-wrapper-Klasse, die eine
close()
die die Helfer in der Nähe, wie ein Spiegel, deropen()
rief getWriteableDatabase und dann migriert zu haben eineContentProvider
. Das Modell fürContentProvider
nicht verwendenSQLiteDatabase.close()
ich denke, das ist eine große Ahnung, wie der code verwendetgetWriteableDatabase
In einigen Fällen war ich immer noch tun, direct access (Bildschirm Validierung Abfragen in der main, so wanderte ich zu einem getWriteableDatabase/rawQuery Modell.Benutze ich ein singleton, und es gibt die etwas ominöse Bemerkung in die enge Dokumentation
(meine Fettformatierung).
Also musste ich Häufig abstürzt wo ich einen hintergrund-threads auf die Datenbank zugreifen und Sie laufen zur gleichen Zeit als Vordergrund zu stellen.
Also ich denke
close()
Kräfte, die Datenbank zu schließen, unabhängig von anderen threads holding-Referenzen - soclose()
sich nicht einfach rückgängig machen, die passendengetWriteableDatabase
aber zwingen schließen alle offene Anfragen. Die meisten der Zeit ist dies kein problem, da der code single-threading, aber in multi-threaded-Fällen gibt es immer die chance, das öffnen und schließen synchron.Gelesen Kommentare anderswo, die erklärt, dass die SqLiteDatabaseHelper code-Beispiel zählt, dann ist die einzige Zeit, die Sie wollen, eine in der Nähe ist, wo Sie wollen, dass die situation, wo Sie wollen eine backup-Kopie, und Sie erzwingen möchten, dass alle verbindungen geschlossen werden, und die Kraft SqLite zu schreiben entfernt alle zwischengespeicherten Sachen, die möglicherweise verwaiste-über - in anderen Worten, beenden Sie alle Anwendung, die Datenbank-Aktivität, schließen Sie nur im Fall, dass der Helfer den Faden verloren hat, führen Sie einen Datei-level-Aktivität (backup/restore) dann wieder von vorn anfangen.
Obwohl es klingt wie eine gute Idee zu versuchen, und schließen Sie in einer kontrollierten Art und Weise, die Realität ist, dass Android behält sich das Recht vor, trash Ihre VM so einer Schließung ist die Verringerung des Risikos von zwischengespeicherten Aktualisierungen nicht geschrieben, aber es kann nicht garantiert werden, wenn das Gerät belastet wird, und wenn Sie richtig befreit Ihren Cursor und Verweise auf Datenbanken (die nicht statische member) dann der Helfer wird die Datenbank geschlossen haben sowieso.
Also mein nehmen ist, dass der Ansatz ist:
Verwenden getWriteableDatabase zu öffnen von einer singleton-wrapper. (Ich habe eine abgeleitete Anwendung ist Klasse, die Anwendung Kontext von einer statischen zu lösen, die Notwendigkeit für einen Kontext).
Nie direkt in der Nähe nennen.
Nie speichern Sie die resultierende Datenbank in ein beliebiges Objekt, das nicht über eine offensichtliche Umfang und verlassen sich auf das "reference counting" auslösen, einen impliziten close().
Wenn dies die Datei-level-handling, bringen alle Datenbank-Aktivität zu stoppen, und rufen Sie dann in der Nähe nur für den Fall es gibt einen außer Kontrolle geratenen thread auf der Annahme, dass Sie schreiben, die richtigen Transaktionen, also den runaway thread fehl und die Datenbank geschlossen wird zumindest über die richtige Geschäfte und nicht als potentiell Datei-Ebene kopieren einer teilweisen Transaktion.
InformationsquelleAutor der Antwort Ian Spencer
Können Sie versuchen, für die Anwendung der neuen Architektur-Ansatz angekündigt bei Google I/O-2017.
Es enthält auch neue ORM-Bibliothek namens Zimmer
Er enthält drei Komponenten: @Entity, @Dao und @Database
User.java
UserDao.java
AppDatabase.java
InformationsquelleAutor der Antwort Zimbo Rodger
Ich weiß, dass die Antwort zu spät, aber der beste Weg zum ausführen von sqlite-Abfragen in android wird über einen benutzerdefinierten content-provider. Auf diese Weise kann die Benutzeroberfläche entkoppelt mit der Datenbank-Klasse(die Klasse, die erweitert die SQLiteOpenHelper Klasse). Auch die Abfragen ausgeführt werden, die in einem hintergrund-thread(Cursor Loader).
InformationsquelleAutor der Antwort Theo