Odayı Kotlin Multiplatform'a Taşıma

Bu dokümanda, mevcut bir Oda uygulamasını (Kotlin Multiplatform (KMP)).

Mevcut bir Android kod tabanındaki oda kullanımlarını ortak bir ortak KMP'ye taşıma modülünün zorluk düzeyi, kullanılan Room API'lere veya kod tabanı zaten eş yordamları kullanıyor. Bu bölümde bazı yol gösterici bilgiler ve ipuçları sunulmaktadır oda kullanımlarını ortak bir modüle taşımaya çalışırken.

Öncelikle aralarındaki farkları ve eksiklikleri Room'un Android sürümü ve KMP sürümü arasındaki daha fazla bilgi edindiniz. Başarılı bir taşıma işlemi özünde yeniden düzenleme içerir. SupportSQLite* API'lerinin kullanımları ve bunları SQLite Driver API'leriyle değiştirme oda beyanları (@Database ek açıklamalı sınıf, DAO'lar, varlıklar vb.) ortak koda dönüştürür.

Devam etmeden önce aşağıdaki bilgileri tekrar inceleyin:

Sonraki bölümlerde başarılı bir dönüşüm için atmanız gereken taşıma.

Destek SQLite'tan SQLite Sürücüsü'ne taşıma

androidx.sqlite.db ürünündeki API'ler yalnızca Android'de kullanılabilir ve tüm kullanımların olması gerekir. ve SQLite Sürücü API'leriyle yeniden düzenlendi. Geriye dönük uyumluluk için ve kullandığınız RoomDatabase, bir SupportSQLiteOpenHelper.Factory (ör. SQLiteDriver ayarlanmamışsa) Oda, "uyumluluk modunda" davranır burada: Destek SQLite ve SQLite Driver API'leri beklendiği gibi çalışır. Bu durumda, tüm Support SQLite öğelerinizi dönüştürmenize gerek kalmaması için artımlı taşıma işlemleri kullanımlarını SQLite Sürücüsü'ne iletir.

Aşağıda, Support SQLite ve SQLite desteğinin yaygın kullanım örnekleri verilmiştir Sürücü muadilleri:

SQLite desteği (kaynak)

Sonuç vermeyen bir sorgu yürütme

val database: SupportSQLiteDatabase = ...
database.execSQL("ALTER TABLE ...")

Sonuç içeren ancak bağımsız değişken içermeyen bir sorgu yürütme

val database: SupportSQLiteDatabase = ...
database.query("SELECT * FROM Pet").use { cursor ->
  while (cusor.moveToNext()) {
    // read columns
    cursor.getInt(0)
    cursor.getString(1)
  }
}

Sonuç ve bağımsız değişkenler içeren bir sorgu yürütme

database.query("SELECT * FROM Pet WHERE id = ?", id).use { cursor ->
  if (cursor.moveToNext()) {
    // row found, read columns
  } else {
    // row not found
  }
}

SQLite Sürücüsü (hedef)

Sonuç vermeyen bir sorgu yürütme

val connection: SQLiteConnection = ...
connection.execSQL("ALTER TABLE ...")

Sonuç içeren ancak bağımsız değişken içermeyen bir sorgu yürütme

val connection: SQLiteConnection = ...
connection.prepare("SELECT * FROM Pet").use { statement ->
  while (statement.step()) {
    // read columns
    statement.getInt(0)
    statement.getText(1)
  }
}

Sonuç ve bağımsız değişkenler içeren bir sorgu yürütme

connection.prepare("SELECT * FROM Pet WHERE id = ?").use { statement ->
  statement.bindInt(1, id)
  if (statement.step()) {
    // row found, read columns
  } else {
    // row not found
  }
}

Veritabanı işlemi API'lerini doğrudan SupportSQLiteDatabase içinde beginTransaction(), setTransactionSuccessful() ve endTransaction(). Bunlara, runInTransaction() kullanılarak Oda üzerinden de erişilebilir. Bunları taşı kullanımları hakkında daha fazla bilgi edinin.

SQLite desteği (kaynak)

İşlem gerçekleştirme (RoomDatabase kullanarak)

val database: RoomDatabase = ...
database.runInTransaction {
  // perform database operations in transaction
}

İşlem gerçekleştirme (SupportSQLiteDatabase kullanarak)

val database: SupportSQLiteDatabase = ...
database.beginTransaction()
try {
  // perform database operations in transaction
  database.setTransactionSuccessful()
} finally {
  database.endTransaction()
}

SQLite Sürücüsü (hedef)

İşlem gerçekleştirme (RoomDatabase kullanarak)

val database: RoomDatabase = ...
database.useWriterConnection { transactor ->
  transactor.immediateTransaction {
    // perform database operations in transaction
  }
}

İşlem gerçekleştirme (SQLiteConnection kullanarak)

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")
}

Geri çağırma ile ilgili çeşitli geçersiz kılma işlemlerinin de sürücü muadillerine taşınması gerekir:

SQLite desteği (kaynak)

Taşıma alt sınıfları

object Migration_1_2 : Migration(1, 2) {
  override fun migrate(db: SupportSQLiteDatabase) {
    // ...
  }
}

Otomatik taşıma spesifikasyonu alt sınıfları

class AutoMigrationSpec_1_2 : AutoMigrationSpec {
  override fun onPostMigrate(db: SupportSQLiteDatabase) {
    // ...
  }
}

Veritabanı geri çağırma alt sınıfları

object MyRoomCallback : RoomDatabase.Callback {
  override fun onCreate(db: SupportSQLiteDatabase) {
    // ...
  }

  override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
    // ...
  }

  override fun onOpen(db: SupportSQLiteDatabase) {
    // ...
  }
}

SQLite Sürücüsü (hedef)

Taşıma alt sınıfları

object Migration_1_2 : Migration(1, 2) {
  override fun migrate(connection: SQLiteConnection) {
    // ...
  }
}

Otomatik taşıma spesifikasyonu alt sınıfları

class AutoMigrationSpec_1_2 : AutoMigrationSpec {
  override fun onPostMigrate(connection: SQLiteConnection) {
    // ...
  }
}

Veritabanı geri çağırma alt sınıfları

object MyRoomCallback : RoomDatabase.Callback {
  override fun onCreate(connection: SQLiteConnection) {
    // ...
  }

  override fun onDestructiveMigration(connection: SQLiteConnection) {
    // ...
  }

  override fun onOpen(connection: SQLiteConnection) {
    // ...
  }
}

Özetlemek gerekirse, aşağıdaki durumlarda SQLiteDatabase kullanımlarını SQLiteConnection ile değiştirin: RoomDatabase, geri çağırma geçersiz kılmalarda olduğu gibi kullanılamaz (onMigrate, onCreate vb.) RoomDatabase varsa temel hesaba erişin RoomDatabase.useReaderConnection ve kullanarak veritabanı bağlantısı - RoomDatabase.useWriterConnection RoomDatabase.openHelper.writtableDatabase.

Engellenen DAO işlevlerini, askıya alma işlevlerine dönüştürün

Room'un KMP sürümü, I/O gerçekleştirmek için eş yordamlardan yararlanır Yapılandırılmış CoroutineContext üzerinde işlem. Bu, sizin için işlevleri askıya almak için engelleyen DAO işlevlerini taşımanız gerekir.

DAO işlevini engelleme (kaynak)

@Query("SELECT * FROM Todo")
fun getAllTodos(): List<Todo>

DAO işlevini askıya alma (hedef)

@Query("SELECT * FROM Todo")
suspend fun getAllTodos(): List<Todo>

Mevcut DAO engelleme işlevlerinin, askıya alma işlevlerine taşınması mevcut kod tabanı halihazırda eş yordamlar içermiyorsa karmaşık hale gelir. Eş yordamları kullanmaya başlamak için Android'de koordinler konusuna bakın inceleyebilirsiniz.

Tepkisel dönüş türlerini Akışa dönüştür

Tüm DAO işlevlerinin askıya alma işlevleri olması gerekmez. Döndürülen DAO işlevleri LiveData veya RxJava'nın Flowable gibi reaktif türleri dönüştürülmemelidir. askıya alabilirsiniz. Ancak LiveData gibi bazı türler KMP değildir. uyumlu olmalıdır. Reaktif döndürme türlerine sahip DAO işlevleri eş yordamlar, eşzamanlı akışlar.

Uyumsuz KMP türü (kaynak)

@Query("SELECT * FROM Todo")
fun getTodosLiveData(): LiveData<List<Todo>>

Uyumlu KMP türü (hedef)

@Query("SELECT * FROM Todo")
fun getTodosFlow(): Flow<List<Todo>>

Akışları kullanmaya başlamak için Android'de Akışlar kullanabilirsiniz.

Eş yordam bağlamı ayarlayın (İsteğe bağlı)

RoomDatabase, paylaşılan uygulamayla isteğe bağlı olarak yapılandırılabilir veritabanı gerçekleştirmek için RoomDatabase.Builder.setQueryExecutor() kullanan yürütücüler anlamına gelir. Yürütücüler KMP ile uyumlu olmadığından Odanın setQueryExecutor() API, yaygın kaynaklar için kullanılamaz. Bunun yerine, RoomDatabase CoroutineContext ile yapılandırılacak. Bağlam, RoomDatabase.Builder.setCoroutineContext() ayarlanmazsa RoomDatabase varsayılan olarak Dispatchers.IO özelliğini kullanacak.

SQLite Sürücüsü ayarlayın

Support SQLite kullanımları, SQLite Sürücü API'lerine taşındıktan sonra sürücüsünün RoomDatabase.Builder.setDriver kullanılarak yapılandırılması gerekir. İlgili içeriği oluşturmak için kullanılan önerilen sürücü BundledSQLiteDriver. Aşağıdakiler için Sürücü uygulamaları sayfasını inceleyin mevcut sürücü uygulamalarının açıklamaları

Özel SupportSQLiteOpenHelper.Factory, şununla yapılandırıldı: RoomDatabase.Builder.openHelperFactory(), KMP'de desteklenmez. özel açık yardımcı tarafından sağlanan özelliklerin SQLite Sürücü arayüzleri.

Oda bildirimlerini taşı

Taşıma işlemi adımlarının çoğu tamamlandıktan sonra Oda taşınabilir. tanımlarını ortak bir kaynak kümesiyle ilişkilendireceğiz. expect / actual stratejilerin şunları yapabileceğini unutmayın: Oda ile ilgili tanımları aşamalı olarak taşımak için kullanılır. Örneğin tüm askıya alınan DAO işlevlerinin askıya alınmasıyla ilgili işlevler, askıya alma işlevlerine ortak kodda boş olan bir expect @Dao ek açıklamalı arayüzü bildirir, ancak Android'de engelleme işlevleri içerir.

// 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
}