本文說明如何將現有的 Room 實作遷移至單一聊天室 都採用 Kotlin Multiplatform (KMP)。
將現有 Android 程式碼集的 Room 使用情形遷移至通用的共用 KMP 根據所使用的 Room API,或是否要 但程式碼集已經在使用協同程式本節提供了一些指引和提示 ,以將 Room 的用量遷移至通用模組。
請務必先瞭解兩者的差異之處
Android 版和 KMP 版本的實用功能
相關的設定基本上,遷移成功牽涉到重構
SupportSQLite*
API 的使用情形,並以 SQLite 驅動程式 API 取代
以及移動的 Room 宣告 (@Database
註解類別、DAO、
這些實體等項目)
請先複習下列資訊,再繼續操作:
以下各節將說明要達到這個目標所需的各種步驟
從 Support SQLite 遷移至 SQLite 驅動程式
androidx.sqlite.db
中的 API 僅適用於 Android,任何用途皆可
透過 SQLite 驅動程式 API 重構享有回溯相容性,且
RoomDatabase
設定為 SupportSQLiteOpenHelper.Factory
(即
未設定 SQLiteDriver
),則 Room 會在「相容模式」下運作在哪?
同時支援 SQLite 和 SQLite 驅動程式 API。這樣一來,
逐步進行遷移作業,讓您不必轉換所有支援服務 SQLite
單一變更項目。
以下為 Support SQLite 及其 SQLite 的常見用法 驅動程式相應版本:
支援 SQLite (來源)
執行沒有結果的查詢
val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")
執行含有結果但不含引數的查詢
val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
while (cusor.moveToNext()) {
// read columns
cursor.getInt(0)
cursor.getString(1)
}
}
使用結果和引數執行查詢
database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
if (cursor.moveToNext()) {
// row found, read columns
} else {
// row not found
}
}
SQLite 驅動程式 (目標)
執行沒有結果的查詢
val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")
執行含有結果但不含引數的查詢
val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
while (statement.step()) {
// read columns
statement.getInt(0)
statement.getText(1)
}
}
使用結果和引數執行查詢
connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
statement.bindInt(1, id)
if (statement.step()) {
// row found, read columns
} else {
// row not found
}
}
資料庫交易 API 可直接在 SupportSQLiteDatabase
中使用,並
beginTransaction()
、setTransactionSuccessful()
和endTransaction()
。
你也可以透過 runInTransaction()
使用聊天室。遷移這些
SQLite 驅動程式 API 的用量。
支援 SQLite (來源)
執行交易 (使用 RoomDatabase
)
val database: RoomDatabase = ...
database.runInTransaction {
// perform database operations in transaction
}
執行交易 (使用 SupportSQLiteDatabase
)
val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
// perform database operations in transaction
database.setTransactionSuccessful()
} finally {
database.endTransaction()
}
SQLite 驅動程式 (目標)
執行交易 (使用 RoomDatabase
)
val database: RoomDatabase = ...
database.useWriterConnection { transactor ->
transactor.immediateTransaction {
// perform database operations in transaction
}
}
執行交易 (使用 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")
}
各種回呼覆寫也必須遷移到其驅動程式對應:
支援 SQLite (來源)
遷移子類別
object Migration_1_2 : Migration(1, 2) {
override fun migrate(db: SupportSQLiteDatabase) {
// ...
}
}
自動遷移規格子類別
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(db: SupportSQLiteDatabase) {
// ...
}
}
資料庫回呼子類別
object MyRoomCallback : RoomDatabase.Callback {
override fun onCreate(db: SupportSQLiteDatabase) {
// ...
}
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
// ...
}
override fun onOpen(db: SupportSQLiteDatabase) {
// ...
}
}
SQLite 驅動程式 (目標)
遷移子類別
object Migration_1_2 : Migration(1, 2) {
override fun migrate(connection: SQLiteConnection) {
// ...
}
}
自動遷移規格子類別
class AutoMigrationSpec_1_2 : AutoMigrationSpec {
override fun onPostMigrate(connection: SQLiteConnection) {
// ...
}
}
資料庫回呼子類別
object MyRoomCallback : RoomDatabase.Callback {
override fun onCreate(connection: SQLiteConnection) {
// ...
}
override fun onDestructiveMigration(connection: SQLiteConnection) {
// ...
}
override fun onOpen(connection: SQLiteConnection) {
// ...
}
}
總結來說,請將 SQLiteDatabase
的用法替換為 SQLiteConnection
,
無法使用 RoomDatabase
,例如在回呼覆寫中 (onMigrate
、
onCreate
等)。如果有 RoomDatabase
可用,則存取基礎
使用 RoomDatabase.useReaderConnection
和
RoomDatabase.useWriterConnection
取代
RoomDatabase.openHelper.writtableDatabase
。
將封鎖 DAO 函式轉換為暫停函式
Room 的 KMP 版本依賴協同程式執行 I/O
針對已設定的 CoroutineContext
執行各項作業。這表示
就不需遷移任何會阻斷的 DAO 函式來暫停函式。
封鎖 DAO 函式 (來自)
@Query("SELECT * FROM Todo")
fun getAllTodos(): List<Todo>
暫停 DAO 函式 (目標)
@Query("SELECT * FROM Todo")
suspend fun getAllTodos(): List<Todo>
您可以將現有 DAO 封鎖函式遷移至暫停函式, 如果現有程式碼集尚未包含協同程式,會變得複雜。 如要開始使用協同程式,請參閱「Android 中的協同程式」 你的程式碼集。
將回應式傳回類型轉換為 Flow
並非所有 DAO 函式都需要暫停函式。會傳回 DAO 函式
不應轉換 LiveData
或 RxJava 的 Flowable
等回應式類型
來暫停函式不過,LiveData
等部分類型並不是 KMP
相容。必須將採用回應式傳回類型的 DAO 函式遷移至
協同程式流程。
不相容的 KMP 類型 (來源)
@Query("SELECT * FROM Todo")
fun getTodosLiveData(): LiveData<List<Todo>>
相容的 KMP 類型 (目標)
@Query("SELECT * FROM Todo")
fun getTodosFlow(): Flow<List<Todo>>
請參閱 Android 中的資料流,開始在 程式碼集
設定協同程式結構定義 (選用)
您可以選擇使用共用應用程式設定 RoomDatabase
使用 RoomDatabase.Builder.setQueryExecutor()
執行資料庫的執行器
作業。由於執行工具與 KMP 不相容,因此 Room 的 setQueryExecutor()
常見來源無法使用 API。相反地,RoomDatabase
必須
也就是使用 CoroutineContext
進行設定您可以使用
RoomDatabase.Builder.setCoroutineContext()
,如果未設定,則為
RoomDatabase
將預設使用 Dispatchers.IO
。
設定 SQLite 驅動程式
將 Support SQLite 的使用情形遷移至 SQLite 驅動程式 API 後,
驅動程式是使用 RoomDatabase.Builder.setDriver
來設定。
建議的驅動程式為 BundledSQLiteDriver
。請參閱「驅動因素實作」
可用驅動程式實作的說明。
自訂 SupportSQLiteOpenHelper.Factory
設定方式:
KMP 不支援 RoomDatabase.Builder.openHelperFactory()
,
自訂開啟輔助程式所提供的功能,必須重新採用
SQLite 驅動程式介面。
移動 Room 宣告
完成大部分的遷移步驟後,您就可以移動會議室
通用來源集的明確定義請注意,expect
/ actual
策略可
可用於逐步移動 Room 相關定義舉例來說
封鎖的 DAO 函式可以遷移至暫停函式,
在常見程式碼中,宣告 expect
@Dao
註解的介面為空白,
包含 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
}