Camera (multipiattaforma Kotlin)

La libreria di persistenza della stanza fornisce un livello di astrazione rispetto a SQLite per consentire per un accesso più affidabile al database sfruttando al contempo tutta la potenza di SQLite. Questo è incentrata sull'utilizzo di Room nei progetti Kotlin Multiplatform (KMP). Per maggiori informazioni informazioni sull'utilizzo della stanza, consulta Salvare i dati in un database locale utilizzando Room o i nostri campioni ufficiali.

Configurazione delle dipendenze

La versione attuale di Room che supporta KMP è 2.7.0-alpha01 o superiore.

Per configurare la stanza nel tuo progetto KMP, aggiungi le dipendenze per gli artefatti nel build.gradle.kts file per il modulo:

  • androidx.room:room-gradle-plugin - Il plug-in Gradle per configurare gli schemi delle stanze
  • androidx.room:room-compiler: il processore KSP che genera il codice
  • androidx.room:room-runtime: la parte relativa al runtime della libreria
  • androidx.sqlite:sqlite-bundled: (facoltativo) la libreria SQLite in bundle

Inoltre, devi configurare il driver SQLite di Room. Questi fattori sono diversi in base alla piattaforma di destinazione. Consulta Implementazioni dei driver per le descrizioni delle implementazioni disponibili dei driver.

Per ulteriori informazioni sulla configurazione, consulta le seguenti risorse:

definisci le classi di database

Devi creare una classe di database annotata con @Database insieme ai DAO ed entità all'interno dell'insieme di origine comune del modulo KMP condiviso. In fase di posizionamento queste classi nelle origini comuni ne consentiranno la condivisione tra tutti i target piattaforme di terze parti.

Quando dichiari un oggetto expect tramite l'interfaccia RoomDatabaseConstructor, il compilatore delle stanze genera il actual implementazioni. Android Studio potrebbe mostrare un avviso "Expected object 'AppDatabaseConstructor' has no actual declaration in module"; you can suppress the warning with@Suppress("NO_ACTUAL_FOR_EXPECT")`.

// shared/src/commonMain/kotlin/Database.kt

@Database(entities = [TodoEntity::class], version = 1)
@ConstructedBy(AppDatabaseConstructor::class)
abstract class AppDatabase : RoomDatabase() {
  abstract fun getDao(): TodoDao
}

// The Room compiler generates the `actual` implementations.
@Suppress("NO_ACTUAL_FOR_EXPECT")
expect object AppDatabaseConstructor : RoomDatabaseConstructor<AppDatabase>

@Dao
interface TodoDao {
  @Insert
  suspend fun insert(item: TodoEntity)

  @Query("SELECT count(*) FROM TodoEntity")
  suspend fun count(): Int

  @Query("SELECT * FROM TodoEntity")
  fun getAllAsFlow(): Flow<List<TodoEntity>>
}

@Entity
data class TodoEntity(
  @PrimaryKey(autoGenerate = true) val id: Long = 0,
  val title: String,
  val content: String
)

Tieni presente che, se vuoi, puoi utilizzare le dichiarazioni effettive / previste. per creare implementazioni di stanze specifiche per la piattaforma. Ad esempio, puoi aggiungere DAO specifico della piattaforma definito nel codice comune utilizzando expect e quindi specificare le definizioni di actual con query aggiuntive in specifiche della piattaforma set di origini dati.

Creazione del generatore di database

Devi definire un generatore di database per creare un'istanza di Room su ogni piattaforma. Questo è l'unica parte dell'API che deve trovarsi nell'origine specifica della piattaforma set di dati a causa delle differenze nelle API del file system. Ad esempio, in Android, la posizione del database è di solito ottenuta API Context.getDatabasePath(), mentre per iOS la posizione del database è ottenerlo utilizzando NSFileManager.

Android

Per creare l'istanza del database, specifica un Contesto insieme al database del tuo percorso di apprendimento.

// shared/src/androidMain/kotlin/Database.kt

fun getDatabaseBuilder(ctx: Context): RoomDatabase.Builder<AppDatabase> {
  val appContext = ctx.applicationContext
  val dbFile = appContext.getDatabasePath("my_room.db")
  return Room.databaseBuilder<AppDatabase>(
    context = appContext,
    name = dbFile.absolutePath
  )
}

iOS

Per creare l'istanza di database, fornisci un percorso del database utilizzando NSFileManager, in genere si trova in NSDocumentDirectory.

// shared/src/iosMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFilePath = documentDirectory() + "/my_room.db"
    return Room.databaseBuilder<AppDatabase>(
        name = dbFilePath,
    )
}

private fun documentDirectory(): String {
  val documentDirectory = NSFileManager.defaultManager.URLForDirectory(
    directory = NSDocumentDirectory,
    inDomain = NSUserDomainMask,
    appropriateForURL = null,
    create = false,
    error = null,
  )
  return requireNotNull(documentDirectory?.path)
}

JVM (desktop)

Per creare l'istanza di database, fornisci un percorso del database utilizzando Java o Kotlin su quelle di livello inferiore.

// shared/src/jvmMain/kotlin/Database.kt

fun getDatabaseBuilder(): RoomDatabase.Builder<AppDatabase> {
    val dbFile = File(System.getProperty("java.io.tmpdir"), "my_room.db")
    return Room.databaseBuilder<AppDatabase>(
        name = dbFile.absolutePath,
    )
}

Creazione di un'istanza del database

Dopo aver ottenuto RoomDatabase.Builder da uno dei provider di servizi puoi configurare il resto del database della stanza con un codice insieme all'istanza effettiva del database.

// shared/src/commonMain/kotlin/Database.kt

fun getRoomDatabase(
    builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
  return builder
      .addMigrations(MIGRATIONS)
      .fallbackToDestructiveMigrationOnDowngrade()
      .setDriver(BundledSQLiteDriver())
      .setQueryCoroutineContext(Dispatchers.IO)
      .build()
}

Selezione di un driver SQLite

Gli snippet di codice precedenti utilizzano BundledSQLiteDriver. Questo è il driver consigliato che include SQLite compilato dall'origine, che fornisce la versione più coerente e aggiornata di SQLite su tutte le piattaforme. Se Se vuoi usare SQLite fornito dal sistema operativo, usa l'API setDriver nelle applicazioni set di origini dati che specificano un driver specifico per la piattaforma. Per Android, puoi utilizzare AndroidSQLiteDriver, mentre per iOS puoi usare NativeSQLiteDriver. A usa NativeSQLiteDriver, devi fornire un'opzione di collegamento in modo che iOS che si collega in modo dinamico al sistema SQLite.

// shared/build.gradle.kts

kotlin {
    listOf(
        iosX64(),
        iosArm64(),
        iosSimulatorArm64()
    ).forEach { iosTarget ->
        iosTarget.binaries.framework {
            baseName = "TodoApp"
            isStatic = true
            // Required when using NativeSQLiteDriver
            linkerOpts.add("-lsqlite3")
        }
    }
}

Differenze

Room è stata originariamente sviluppata come libreria Android, per poi essere migrata in KMP con particolare attenzione alla compatibilità delle API. La versione KMP di Room è leggermente diversa tra piattaforme e dalla versione specifica per Android. Queste differenze sono elencate e descritte come segue.

Blocco delle funzioni DAO

Quando utilizzi Room per KMP, tutte le funzioni DAO compilate per piattaforme non Android devono essere funzioni suspend con l'eccezione dei tipi restituiti reattivi, come come Flow.

// shared/src/commonMain/kotlin/MultiplatformDao.kt

@Dao
interface MultiplatformDao {
  // ERROR: Blocking function not valid for non-Android targets
  @Query("SELECT * FROM Entity")
  fun blockingQuery(): List<Entity>

  // OK
  @Query("SELECT * FROM Entity")
  suspend fun query(): List<Entity>

  // OK
  @Query("SELECT * FROM Entity")
  fun queryFlow(): Flow<List<Entity>>

  // ERROR: Blocking function not valid for non-Android targets
  @Transaction
  fun blockingTransaction() { // … }

  // OK
  @Transaction
  suspend fun transaction() { // … }
}

La stanza sfrutta i vantaggi della libreria kotlinx.coroutines asincrona ricca di funzionalità offerti da Kotlin su più piattaforme. Per una funzionalità ottimale, suspend vengono applicate per i DAO compilati in un progetto KMP, ad eccezione delle DAO specifici di Android per mantenere la compatibilità con le versioni precedenti codebase.

Differenze di funzionalità in KMP

Questa sezione descrive le differenze tra le funzionalità di KMP e la piattaforma Android diverse versioni di Room.

Funzioni DAO @RawQuery

Funzioni annotate con @RawQuery compilate per piattaforme non Android è necessario dichiarare un parametro di tipo RoomRawQuery anziché SupportSQLiteQuery.

@Dao
interface TodoDao {
  @RawQuery
  suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}

È quindi possibile utilizzare un RoomRawQuery per creare una query in fase di runtime:

suspend fun getTodosWithLowercaseTitle(title: String): List<TodoEntity> {
  val query = RoomRawQuery(
    sql = "SELECT * FROM TodoEntity WHERE title = ?"
    onBindStatement = {
      it.bindText(1, title.lowercase())
    }
  )
  return todosDao.getTodos(query)
}

Callback della query

Le seguenti API per la configurazione dei callback delle query non sono disponibili in comune e non sono quindi disponibili su piattaforme diverse da Android.

  • RoomDatabase.Builder.setQueryCallback
  • RoomDatabase.QueryCallback

Abbiamo intenzione di aggiungere il supporto per il callback delle query in una versione futura di Room.

L'API per configurare un RoomDatabase con un callback di query RoomDatabase.Builder.setQueryCallback insieme all'interfaccia di callback RoomDatabase.QueryCallback non sono disponibili in comune e pertanto non sono disponibili su altre piattaforme diverse da Android.

Database con chiusura automatica

L'API per abilitare la chiusura automatica dopo un timeout, RoomDatabase.Builder.setAutoCloseTimeout è disponibile solo su Android ed è non disponibile in altre piattaforme.

Database predefinito

Le seguenti API per creare un RoomDatabase utilizzando un database esistente (ad es. predefiniti) non sono disponibili in comune e pertanto non lo sono su altre piattaforme diverse da Android. Queste API sono:

  • RoomDatabase.Builder.createFromAsset
  • RoomDatabase.Builder.createFromFile
  • RoomDatabase.Builder.createFromInputStream
  • RoomDatabase.PrepackagedDatabaseCallback

Abbiamo intenzione di aggiungere il supporto per i database predefiniti in una versione futura di Stanza.

Annullamento della convalida di più istanze

L'API per abilitare l'annullamento della convalida di più istanze, RoomDatabase.Builder.enableMultiInstanceInvalidation è disponibile solo su Android e non è disponibile né nelle piattaforme standard né in altre.