Z tego dokumentu dowiesz się, jak przenieść dotychczasową implementację sal do który wykorzystuje platformę Kotlin Multiplatform (KMP).
Migracja danych o wykorzystaniu sal z istniejącej bazy kodu Androida do wspólnej współdzielonej platformy KMP mogą mieć różne trudności w zależności od używanych interfejsów API sal baza kodu korzysta już z Coroutines. W tej sekcji znajdziesz wskazówki i wskazówki podczas próby przeniesienia wykorzystania pokoju do wspólnego modułu.
Warto najpierw zapoznać się z różnicami
między wersją pokoju na Androida a wersją KMP,
wymaganych czynnościach. Ogólnie udana migracja wymaga refaktoryzacji.
zastosowania interfejsów API SupportSQLite*
i zastąpienie ich interfejsami SQLite Driver API
wraz z przenoszeniem deklaracji sal (klasa z adnotacjami: @Database
, DAO,
encji itd.) we wspólny kod.
Zanim przejdziesz dalej, sprawdź te informacje:
W kolejnych sekcjach opisano różne kroki wymagane do skutecznego migracji danych.
Przejście z Support SQLite na sterownik SQLite
Interfejsy API w androidx.sqlite.db
są przeznaczone tylko na Androida i trzeba je
zmodyfikowano za pomocą interfejsów SQLite Driver API. Aby zapewnić zgodność wsteczną,
RoomDatabase
jest skonfigurowany z zastosowaniem SupportSQLiteOpenHelper.Factory
(tzn.
nie ustawiono SQLiteDriver
), sala działa w „trybie zgodności” gdzie
Interfejsy API obsługi SQLite i SQLite Driver API działają zgodnie z oczekiwaniami. Dzięki temu
migracje przyrostowe, dzięki którym nie trzeba konwertować całego pomocniczego SQLite
do sterownika SQLite w jednej zmianie.
Poniższe przykłady przedstawiają typowe zastosowania Support SQLite i ich SQLite Odpowiedniki kierowców:
Obsługa SQLite (od)
Wykonaj zapytanie bez wyniku
val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")
Wykonaj zapytanie z wynikiem bez argumentów
val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
while (cusor.moveToNext()) {
// read columns
cursor.getInt(0)
cursor.getString(1)
}
}
Wykonaj zapytanie z wynikiem i argumentami
database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
if (cursor.moveToNext()) {
// row found, read columns
} else {
// row not found
}
}
Sterownik SQLite (do)
Wykonaj zapytanie bez wyniku
val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")
Wykonaj zapytanie z wynikiem bez argumentów
val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
while (statement.step()) {
// read columns
statement.getInt(0)
statement.getText(1)
}
}
Wykonaj zapytanie z wynikiem i argumentami
connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
statement.bindInt(1, id)
if (statement.step()) {
// row found, read columns
} else {
// row not found
}
}
Interfejsy API transakcji baz danych są dostępne bezpośrednio w SupportSQLiteDatabase
z użyciem
beginTransaction()
, setTransactionSuccessful()
i endTransaction()
.
Są one też dostępne w pokoju za pomocą runInTransaction()
. Przenieś te
i zastosowaniach w interfejsach SQLite Driver API.
Obsługa SQLite (od)
Wykonaj transakcję (przy użyciu aplikacji RoomDatabase
)
val database: RoomDatabase = ...
database.runInTransaction {
// perform database operations in transaction
}
Wykonaj transakcję (przy użyciu aplikacji SupportSQLiteDatabase
)
val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
// perform database operations in transaction
database.setTransactionSuccessful()
} finally {
database.endTransaction()
}
Sterownik SQLite (do)
Wykonaj transakcję (przy użyciu aplikacji RoomDatabase
)
val database: RoomDatabase = ...
database.useWriterConnection { transactor ->
transactor.immediateTransaction {
// perform database operations in transaction
}
}
Wykonaj transakcję (przy użyciu aplikacji 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")
}
Zastąpienie różnych wywołań zwrotnych trzeba też przenieść do odpowiednich odpowiedników:
Obsługa SQLite (od)
Podklasy migracji
object Migration_1_2 : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
// ...
}
}
Podklasy specyfikacji automatycznej migracji
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
// ...
}
}
Podklasy wywołania zwrotnego bazy danych
object MyRoomCallback : RoomDatabase.Callback {
override fun onCreate(db: SupportSQLiteDatabase) {
// ...
}
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
// ...
}
override fun onOpen(db: SupportSQLiteDatabase) {
// ...
}
}
Sterownik SQLite (do)
Podklasy migracji
object Migration_1_2 : Migration(1, 2) {
override fun migrate(connection: SQLiteConnection) {
// ...
}
}
Podklasy specyfikacji automatycznej migracji
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(connection: SQLiteConnection) {
// ...
}
}
Podklasy wywołania zwrotnego bazy danych
object MyRoomCallback : RoomDatabase.Callback {
override fun onCreate(connection: SQLiteConnection) {
// ...
}
override fun onDestructiveMigration(connection: SQLiteConnection) {
// ...
}
override fun onOpen(connection: SQLiteConnection) {
// ...
}
}
Podsumowując, zastąp zastosowania SQLiteDatabase
ciągiem SQLiteConnection
, gdy
Metoda RoomDatabase
jest niedostępna, np. w zastąpieniach wywołania zwrotnego (onMigrate
,
onCreate
itp.). Jeśli usługa RoomDatabase
jest dostępna, przejdź do źródła
połączenie z bazą danych za pomocą RoomDatabase.useReaderConnection
i
RoomDatabase.useWriterConnection
zamiast
RoomDatabase.openHelper.writtableDatabase
Przekonwertuj blokujące funkcje DAO na zawieszanie funkcji
Wersja pokoju w KMP wykorzystuje współrzędne do przeprowadzania I/O
operacji na skonfigurowanym elemencie CoroutineContext
. Oznacza to, że musisz
musisz przenieść blokujące funkcje DAO do zawieszania funkcji.
Blokowanie funkcji DAO (z)
@Query("SELECT * FROM Todo")
fun getAllTodos(): List<Todo>
Zawieszam funkcję DAO (do)
@Query("SELECT * FROM Todo")
suspend fun getAllTodos(): List<Todo>
Migracja istniejących funkcji blokujących DAO do funkcji zawieszania może być jeśli bieżąca baza kodu nie zawiera jeszcze współrzędnych. Aby dowiedzieć się, jak korzystać z współrzędnych, zapoznaj się z informacjami o kogutynach w Androidzie. w bazie kodu.
Przekształć typy zwrotów reaktywnych na Flow
Nie wszystkie funkcje DAO muszą być funkcjami zawieszania. Funkcje DAO, które zwracają
typów reaktywnych, takich jak LiveData
czy Flowable
RxJava, nie należy konwertować
do zawieszania funkcji. Jednak niektóre typy, takie jak LiveData
, nie są obsługiwane przez KMP
są zgodne. Funkcje DAO z reaktywnymi typami zwrotów trzeba przenieść do
współrzędnych przepływów.
Niezgodny typ KMP (z)
@Query("SELECT * FROM Todo")
fun getTodosLiveData(): LiveData<List<Todo>>
Zgodny typ KMP (do)
@Query("SELECT * FROM Todo")
fun getTodosFlow(): Flow<List<Todo>>
Aby zacząć korzystać z przepływów w aplikacji, przeczytaj artykuł Flows in Android. bazy kodu.
Ustawianie kontekstu Coroutine (opcjonalnie)
Interfejs RoomDatabase
można opcjonalnie skonfigurować ze współdzieloną aplikacją
wykonawcy używający RoomDatabase.Builder.setQueryExecutor()
do wykonywania bazy danych
operacji. Wykonawcy nie są zgodne z KMP, więc setQueryExecutor()
Room
Interfejs API jest niedostępny dla popularnych źródeł. Zamiast tego RoomDatabase
musi
skonfigurować za pomocą CoroutineContext
. Kontekst można określić za pomocą funkcji
RoomDatabase.Builder.setCoroutineContext()
, jeśli nie ustawisz żadnej,
RoomDatabase
będzie domyślnie używać Dispatchers.IO
.
Ustaw sterownik SQLite
Po przeniesieniu zastosowań pomocniczych SQLite do interfejsów SQLite Driver API
sterownik należy skonfigurować za pomocą programu RoomDatabase.Builder.setDriver
.
zalecany sterownik to BundledSQLiteDriver
. Zobacz Implementacje sterowników dla:
opisy dostępnych wdrożeń sterowników.
Niestandardowy zasób SupportSQLiteOpenHelper.Factory
skonfigurowany za pomocą
Parametry RoomDatabase.Builder.openHelperFactory()
nie są obsługiwane w KMP,
funkcji udostępnianych przez niestandardowy otwarty pomocnik trzeba będzie ponownie zaimplementować za pomocą
Interfejsy SQLite Driver.
Przenoszenie deklaracji dotyczących sal
Po zakończeniu większości etapów migracji można przenieść pokój
do wspólnego zbioru źródłowego. Pamiętaj, że strategie expect
/ actual
mogą
służą do stopniowego przenoszenia definicji związanych z salami. Jeśli na przykład nie wszystkie
blokujące funkcje DAO można przenieść do zawieszania funkcji, istnieje możliwość
zadeklarować interfejs z adnotacjami expect
@Dao
, który jest pusty w kodzie wspólnym, ale
zawiera funkcje blokujące w Androidzie.
// 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
}