Camera (multipiattaforma Kotlin)

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

Configurazione delle dipendenze

La versione corrente della stanza che supporta KMP è 2.7.0-alpha01 o versioni successive.

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

  • androidx.room:room-gradle-plugin: il plug-in Gradle per configurare gli schemi delle stanze
  • androidx.room:room-compiler: il processore del principale punto di forza che genera il codice
  • androidx.room:room-runtime: la parte runtime della libreria
  • androidx.sqlite:sqlite-bundled: (facoltativo) la libreria SQLite in bundle

Inoltre, devi configurare il driver SQLite della stanza virtuale. Questi fattori variano in base alla piattaforma di destinazione. Consulta Implementazioni dei driver per le descrizioni delle implementazioni disponibili.

Per ulteriori informazioni sulla configurazione, consulta le seguenti risorse:

Definizione delle classi di database

Devi creare una classe di database annotata con @Database insieme a DAO ed entità all'interno del set di origine comune del tuo modulo KMP condiviso. Se posizioni questi corsi in origini comuni, potrai condividerli tra tutte le piattaforme di destinazione.

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

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

@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 puoi utilizzare dichiarazioni effettive / attese per creare implementazioni di stanze specifiche per piattaforma. Ad esempio, puoi aggiungere un DAO specifico per piattaforma definito nel codice comune utilizzando expect e poi specificare le definizioni di actual con query aggiuntive in set di origini specifici della piattaforma.

Creazione del generatore di database

Devi definire un generatore di database per creare un'istanza della stanza su ogni piattaforma. Questa è l'unica parte dell'API che deve essere in set di origini specifici della piattaforma a causa delle differenze nelle API del file system. Ad esempio, in Android la posizione del database si ottiene solitamente tramite l'API Context.getDatabasePath(), mentre per iOS la posizione del database si ottiene utilizzando NSHomeDirectory.

Android

Per creare l'istanza di database, specifica un Contesto e il percorso del database. Non è necessario specificare una fabbrica di database.

// 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, indica un valore di fabbrica del database insieme al percorso del database. La fabbrica del database è una funzione lambda che richiama una funzione di estensione generata il cui nome è instantiateImpl con un ricevitore di tipo KClass<T>, dove T è il tipo della classe annotata @Database.

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

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

JVM (computer)

Per creare l'istanza di database, specifica solo il percorso del database. Non è necessario specificare un valore di fabbrica per il database.

// shared/src/commonMain/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,
    )
}

Istanza del database

Dopo aver ottenuto il RoomDatabase.Builder da uno dei costruttori specifici della piattaforma, puoi configurare il resto del database delle stanze nel codice comune insieme all'effettiva istanza 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 SQLiteDriver

Gli snippet di codice precedenti utilizzano il parametro 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 vuoi utilizzare l'API SQLite fornita dal sistema operativo, usa l'API setDriver in set di origini specifici della piattaforma che specificano un driver specifico per la piattaforma. Per Android, puoi usare AndroidSQLiteDriver, mentre per iOS puoi usare NativeSQLiteDriver. Per utilizzare NativeSQLiteDriver, devi fornire un'opzione di linker in modo che l'app per iOS si colleghi 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 è stato originariamente sviluppato come libreria Android, ma è stata successivamente migrata a KMP con particolare attenzione alla compatibilità delle API. La versione KMP di Room è leggermente diversa a seconda delle piattaforme e della versione specifica per Android. Queste differenze sono elencate e descritte come segue.

Blocco delle funzioni DAO

Quando si utilizza Room for KMP, tutte le funzioni DAO compilate per piattaforme non Android devono essere funzioni suspend, ad eccezione dei tipi restituiti reattivi, 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 camera beneficia della libreria kotlinx.coroutines asincrona, ricca di funzionalità offerta da Kotlin per diverse piattaforme. Per una funzionalità ottimale, le funzioni suspend vengono applicate ai DAO compilati in un progetto KMP, ad eccezione dei DAO specifici di Android per mantenere la compatibilità con le versioni precedenti con il codebase esistente.

Differenze delle funzionalità con KMP

Questa sezione descrive in che modo le funzionalità differiscono tra le versioni della piattaforma KMP e Android di Room.

Funzioni DAO @RawQuery

Le funzioni annotate con @RawQuery e compilate per piattaforme non Android generano un errore. Abbiamo intenzione di aggiungere il supporto per @RawQuery in una versione futura della stanza virtuale.

Callback della query

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

  • RoomDatabase.Builder.setQueryCallback
  • RoomDatabase.QueryCallback

Intendiamo aggiungere il supporto per il callback delle query in una versione futura della stanza virtuale.

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

Chiusura automatica del database

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

Database predefinito

Le seguenti API per creare un RoomDatabase utilizzando un database esistente (ad esempio un database predefinito) non sono disponibili comuni e, pertanto, non lo sono in 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 della stanza.

Annullamento 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 tra le piattaforme comuni né in altre piattaforme.