In diesem Dokument wird beschrieben, wie Sie eine vorhandene Chatroom-Implementierung zu einer die Kotlin Multiplatform (KMP) verwendet.
Raumnutzungen in einer vorhandenen Android-Codebasis zu einem gemeinsamen gemeinsamen KMP migrieren kann je nach den verwendeten Room APIs oder davon, ob verwendet die Codebasis bereits Koroutinen. In diesem Abschnitt finden Sie Informationen und Tipps, wenn Sie versuchen, Raumnutzung zu einem gemeinsamen Modul zu migrieren.
Es ist wichtig, dass Sie sich zunächst mit den Unterschieden und fehlenden
zwischen der Android-Version von Room und der KMP-Version sowie
die damit einhergehende Einrichtung. Im Wesentlichen bedeutet eine erfolgreiche Migration eine Refaktorierung
wie die SupportSQLite*
APIs eingesetzt werden und durch SQLite-Treiber-APIs ersetzt werden
zusammen mit den verschobenen Raumdeklarationen (@Database
mit Anmerkungen versehene Klasse, DAOs,
Entitäten usw.) in gemeinsamen Code umwandeln.
Lesen Sie sich die folgenden Informationen noch einmal durch, bevor Sie fortfahren:
In den nächsten Abschnitten werden die verschiedenen Schritte beschrieben, die für eine erfolgreiche Migration.
Von Support SQLite zu SQLite-Treiber migrieren
Die APIs in androidx.sqlite.db
sind nur für Android verfügbar und die Nutzung muss
mit SQLite-Treiber-APIs refaktoriert. Für die Abwärtskompatibilität und
Das RoomDatabase
ist mit einem SupportSQLiteOpenHelper.Factory
konfiguriert (d.h.
keine SQLiteDriver
festgelegt ist), verhält sich der Raum im Kompatibilitätsmodus Wo?
unterstützen sowohl die SQLite- als auch die SQLite-Treiber-APIs. Dies ermöglicht
Inkrementelle Migrationen durchführen, sodass Sie nicht alle Ihre SQLite-
Nutzungen in den SQLite-Treiber.
Die folgenden Beispiele zeigen häufige Verwendungen von Support SQLite und deren SQLite. Aussteller der Fahrer:
SQLite unterstützen (von)
Abfrage ohne Ergebnis ausführen
val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")
Abfrage mit Ergebnis, aber ohne Argumenten ausführen
val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
while (cusor.moveToNext()) {
// read columns
cursor.getInt(0)
cursor.getString(1)
}
}
Abfrage mit Ergebnis und Argumenten ausführen
database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
if (cursor.moveToNext()) {
// row found, read columns
} else {
// row not found
}
}
SQLite-Treiber (nach)
Abfrage ohne Ergebnis ausführen
val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")
Abfrage mit Ergebnis, aber ohne Argumenten ausführen
val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
while (statement.step()) {
// read columns
statement.getInt(0)
statement.getText(1)
}
}
Abfrage mit Ergebnis und Argumenten ausführen
connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
statement.bindInt(1, id)
if (statement.step()) {
// row found, read columns
} else {
// row not found
}
}
Datenbanktransaktions-APIs sind direkt in SupportSQLiteDatabase
verfügbar mit
beginTransaction()
, setTransactionSuccessful()
und endTransaction()
.
Sie sind auch über den Chatroom (runInTransaction()
) verfügbar. Diese migrieren
Nutzung von SQLite-Treiber-APIs.
SQLite unterstützen (von)
Transaktion ausführen (mit RoomDatabase
)
val database: RoomDatabase = ...
database.runInTransaction {
// perform database operations in transaction
}
Transaktion ausführen (mit SupportSQLiteDatabase
)
val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
// perform database operations in transaction
database.setTransactionSuccessful()
} finally {
database.endTransaction()
}
SQLite-Treiber (nach)
Transaktion ausführen (mit RoomDatabase
)
val database: RoomDatabase = ...
database.useWriterConnection { transactor ->
transactor.immediateTransaction {
// perform database operations in transaction
}
}
Transaktion ausführen (mit SQLiteConnection
)
val connection: SQLiteConnection = ...
connection.execSQL("BEGIN IMMEDIATE TRANSACTION")
try {
// perform database operations in transaction
connection.execSQL("END TRANSACTION")
} catch(t: Throwable) {
connection.execSQL("ROLLBACK TRANSACTION")
}
Außerdem müssen verschiedene Callback-Überschreibungen zu ihren Treiber-Entsprechungen migriert werden:
SQLite unterstützen (von)
Abgeleitete Migrationsklassen
object Migration_1_2 : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
// ...
}
}
Abgeleitete Klassen der automatischen Migrationsspezifikation
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
// ...
}
}
Abgeleitete Datenbank-Callback-Klassen
object MyRoomCallback : RoomDatabase.Callback {
override fun onCreate(db: SupportSQLiteDatabase) {
// ...
}
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
// ...
}
override fun onOpen(db: SupportSQLiteDatabase) {
// ...
}
}
SQLite-Treiber (nach)
Abgeleitete Migrationsklassen
object Migration_1_2 : Migration(1, 2) {
override fun migrate(connection: SQLiteConnection) {
// ...
}
}
Abgeleitete Klassen der automatischen Migrationsspezifikation
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(connection: SQLiteConnection) {
// ...
}
}
Abgeleitete Datenbank-Callback-Klassen
object MyRoomCallback : RoomDatabase.Callback {
override fun onCreate(connection: SQLiteConnection) {
// ...
}
override fun onDestructiveMigration(connection: SQLiteConnection) {
// ...
}
override fun onOpen(connection: SQLiteConnection) {
// ...
}
}
Fassen wir zusammen: Ersetzen Sie Verwendungen von SQLiteDatabase
durch SQLiteConnection
, wenn
RoomDatabase
ist nicht verfügbar, zum Beispiel in Callback-Überschreibungen (onMigrate
,
onCreate
usw.). Wenn ein RoomDatabase
verfügbar ist, greifen Sie auf die zugrunde liegende
Datenbankverbindung über RoomDatabase.useReaderConnection
und
RoomDatabase.useWriterConnection
statt
RoomDatabase.openHelper.writtableDatabase
.
Blockierende DAO-Funktionen in Sperrungsfunktionen umwandeln
Die KMP-Version von Room nutzt Koroutinen für die E/A-Ausführung
Vorgänge für die konfigurierte CoroutineContext
ausführen. Das bedeutet, dass Sie
alle blockierenden DAO-Funktionen
migrieren, um Funktionen auszusetzen.
DAO-Funktion blockieren (von)
@Query("SELECT * FROM Todo")
fun getAllTodos(): List<Todo>
DAO-Funktion anhalten (to)
@Query("SELECT * FROM Todo")
suspend fun getAllTodos(): List<Todo>
Das Migrieren vorhandener DAO-Blockierfunktionen zu Sperrfunktionen komplizierter wäre, wenn die vorhandene Codebasis nicht bereits Koroutinen enthält. Weitere Informationen zur Verwendung von Koroutinen finden Sie unter Coroutines in Android. in Ihrer Codebasis.
Reaktive Rückgabetypen in Flow umwandeln
Nicht alle DAO-Funktionen müssen Anhaltefunktionen sein. DAO-Funktionen, die
Reaktive Typen wie LiveData
oder Flowable
von RxJava sollten nicht konvertiert werden
um Funktionen auszusetzen. Einige Typen, z. B. LiveData
, sind jedoch keine KMP.
kompatibel sind. DAO-Funktionen mit reaktiven Rückgabetypen müssen migriert werden zu
Koroutineabläufe.
Inkompatibler KMP-Typ (von)
@Query("SELECT * FROM Todo")
fun getTodosLiveData(): LiveData<List<Todo>>
Kompatibler KMP-Typ (bis)
@Query("SELECT * FROM Todo")
fun getTodosFlow(): Flow<List<Todo>>
Weitere Informationen zur Verwendung von Abläufen in Ihrer App finden Sie unter Abläufe in Android. Codebasis.
Koroutine-Kontext festlegen (optional)
Eine RoomDatabase
kann optional mit einer gemeinsam genutzten Anwendung konfiguriert werden
Executors, die RoomDatabase.Builder.setQueryExecutor()
zum Ausführen der Datenbank verwenden
Geschäftsabläufe. Da Executors nicht KMP-kompatibel sind, wird setQueryExecutor()
von Room
Die API ist für allgemeine Quellen nicht verfügbar. Stattdessen muss der RoomDatabase
mit einem CoroutineContext
konfiguriert werden. Ein Kontext kann festgelegt werden mithilfe von
RoomDatabase.Builder.setCoroutineContext()
, wenn keiner festgelegt ist,
RoomDatabase
verwendet standardmäßig Dispatchers.IO
.
SQLite-Treiber einrichten
Nachdem Support-SQLite-Nutzungen zu SQLite-Treiber-APIs migriert wurden,
Treiber muss mit RoomDatabase.Builder.setDriver
konfiguriert werden. Die
empfohlener Treiber ist BundledSQLiteDriver
. Weitere Informationen finden Sie unter Treiberimplementierungen für
Beschreibungen der verfügbaren Treiberimplementierungen.
Benutzerdefinierte SupportSQLiteOpenHelper.Factory
konfiguriert mit
RoomDatabase.Builder.openHelperFactory()
werden im KMP nicht unterstützt.
die vom benutzerdefinierten Open Helper zur Verfügung gestellt werden, müssen mit
SQLite-Treiberschnittstellen.
Deklarationen für den Raum verschieben
Sobald die meisten Migrationsschritte abgeschlossen sind, kann der Chatroom verschoben werden.
mit einem gemeinsamen Quellsatz. Die Strategien expect
/ actual
können
um raumbezogene Definitionen schrittweise zu verschieben. Wenn beispielsweise nicht alle
blockierende DAO-Funktionen in
Aussetzungsfunktionen migriert werden,
eine mit expect
@Dao
annotierte Schnittstelle deklarieren, die im allgemeinen Code leer ist, aber
Blockierfunktionen in Android.
// shared/src/commonMain/kotlin/Database.kt
@Database(entities = [TodoEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun getDao(): TodoDao
abstract fun getBlockingDao(): BlockingTodoDao
}
@Dao
interface TodoDao {
@Query("SELECT count(*) FROM TodoEntity")
suspend fun count(): Int
}
@Dao
expect interface BlockingTodoDao
// shared/src/androidMain/kotlin/BlockingTodoDao.kt
@Dao
actual interface BlockingTodoDao {
@Query("SELECT count(*) FROM TodoEntity")
fun count(): Int
}