ไลบรารีการคงอยู่ของห้องจะมีเลเยอร์ Abstraction เหนือ SQLite เพื่อให้ เพื่อการเข้าถึงฐานข้อมูลที่มีประสิทธิภาพมากขึ้นพร้อมกับใช้ประโยชน์จาก SQLite อย่างเต็มที่ ช่วงเวลานี้ มุ่งเน้นไปที่การใช้ Room ในโปรเจ็กต์ Kotlin Multiplatform (KMP) สำหรับข้อมูลเพิ่มเติม ข้อมูลเกี่ยวกับการใช้ห้องแชทที่หัวข้อบันทึกข้อมูลในฐานข้อมูลในเครื่องโดยใช้ห้องแชท หรือตัวอย่างอย่างเป็นทางการของเรา
การตั้งค่าทรัพยากร Dependency
ห้องแชทเวอร์ชันปัจจุบันที่รองรับ KMP คือ 2.7.0-alpha01 ขึ้นไป
หากต้องการตั้งค่าห้องในโปรเจ็กต์ KMP ให้เพิ่มทรัพยากร Dependency สำหรับอาร์ติแฟกต์ใน
build.gradle.kts
ไฟล์สำหรับโมดูลของคุณ:
androidx.room:room-gradle-plugin
- ปลั๊กอิน Gradle สำหรับกำหนดค่าสคีมาของ Roomandroidx.room:room-compiler
- โปรเซสเซอร์ KSP ที่สร้างโค้ดandroidx.room:room-runtime
- ส่วนรันไทม์ของไลบรารีandroidx.sqlite:sqlite-bundled
- (ไม่บังคับ) ไลบรารี SQLite ที่รวมอยู่ในแพ็กเกจ
นอกจากนี้คุณต้องกำหนดค่าไดรเวอร์ SQLite ของห้องด้วย ปัจจัยเหล่านี้จะแตกต่างกัน โดยอิงตามแพลตฟอร์มเป้าหมาย โปรดดู การติดตั้งใช้งานไดรเวอร์ เพื่อดูคำอธิบายเกี่ยวกับการใช้งานไดรเวอร์ที่ใช้ได้
โปรดดูข้อมูลการตั้งค่าเพิ่มเติมดังต่อไปนี้
- ตั้งค่าตำแหน่งสคีมาโดยใช้ปลั๊กอิน Room Gradle
- KSP กับ Kotlin Multiplatform
- การเพิ่มทรัพยากร Dependency ของรันไทม์
การกำหนดคลาสของฐานข้อมูล
คุณต้องสร้างคลาสฐานข้อมูลที่มีคำอธิบายประกอบด้วย @Database
พร้อมด้วย DAO
และเอนทิตีภายในชุดแหล่งที่มาทั่วไปของโมดูล KMP ที่แชร์ การวาง
คลาสเหล่านี้ในแหล่งที่มาทั่วไปจะช่วยให้แชร์กับทุกเป้าหมายได้
ใหม่
เมื่อคุณประกาศออบเจ็กต์ expect
โดยใช้อินเทอร์เฟซ
RoomDatabaseConstructor
คอมไพเลอร์ของ Room จะสร้าง actual
การนำไปใช้งานจริง Android Studio อาจออกคำเตือน
"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
)
โปรดทราบว่าคุณสามารถเลือกใช้การประกาศจริง / การประกาศที่คาดไว้
เพื่อสร้างการใช้งานห้องแชทเฉพาะแพลตฟอร์ม ตัวอย่างเช่น คุณสามารถเพิ่ม
DAO เฉพาะแพลตฟอร์มที่กำหนดไว้ในโค้ดทั่วไปโดยใช้ expect
แล้วตามด้วย
ระบุคำจำกัดความของ actual
พร้อมด้วยคำค้นหาเพิ่มเติมเฉพาะแพลตฟอร์ม
ชุดแหล่งที่มา
การสร้างเครื่องมือสร้างฐานข้อมูล
คุณต้องกำหนดเครื่องมือสร้างฐานข้อมูลเพื่อสร้างอินสแตนซ์ห้องแชทในแต่ละแพลตฟอร์ม ช่วงเวลานี้
เป็นเพียงส่วนเดียวของ API ที่จำเป็นต้องอยู่ในแหล่งที่มาเฉพาะแพลตฟอร์ม
เนื่องจาก API ระบบไฟล์แตกต่างกัน ตัวอย่างเช่น ใน Android
ตำแหน่งฐานข้อมูลมักจะได้มาจาก
Context.getDatabasePath()
API ในขณะที่สำหรับ iOS ตำแหน่งฐานข้อมูลคือ
จะได้รับโดยใช้ NSFileManager
Android
หากต้องการสร้างอินสแตนซ์ฐานข้อมูล ให้ระบุบริบทพร้อมกับฐานข้อมูล เส้นทาง
// 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
หากต้องการสร้างอินสแตนซ์ฐานข้อมูล ให้ระบุเส้นทางฐานข้อมูลโดยใช้
NSFileManager
ซึ่งปกติจะอยู่ใน 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 (เดสก์ท็อป)
หากต้องการสร้างอินสแตนซ์ฐานข้อมูล โปรดระบุเส้นทางฐานข้อมูลโดยใช้ Java หรือ Kotlin API
// 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,
)
}
การสร้างอินสแตนซ์ฐานข้อมูล
เมื่อคุณได้รับ RoomDatabase.Builder
จากแพลตฟอร์มใดแพลตฟอร์มหนึ่งโดยเฉพาะ
คุณสามารถกำหนดค่าฐานข้อมูลห้องที่เหลือในโค้ดทั่วไปได้
พร้อมกับการสร้างฐานข้อมูลจริง
// shared/src/commonMain/kotlin/Database.kt
fun getRoomDatabase(
builder: RoomDatabase.Builder<AppDatabase>
): AppDatabase {
return builder
.addMigrations(MIGRATIONS)
.fallbackToDestructiveMigrationOnDowngrade()
.setDriver(BundledSQLiteDriver())
.setQueryCoroutineContext(Dispatchers.IO)
.build()
}
การเลือก SQLiteDriver
ข้อมูลโค้ดก่อนหน้าใช้ BundledSQLiteDriver
นี่คือ
ไดรเวอร์ที่แนะนำซึ่งมี SQLite ที่คอมไพล์จากแหล่งที่มา ซึ่งให้
SQLite เวอร์ชันที่สอดคล้องกันและเป็นปัจจุบันที่สุดในทุกแพลตฟอร์ม หากคุณ
ต้องการใช้ SQLite ที่ระบบปฏิบัติการมีให้ ให้ใช้ API ของ setDriver
เฉพาะแพลตฟอร์ม
ชุดแหล่งที่มาที่ระบุไดรเวอร์เฉพาะแพลตฟอร์ม สำหรับ Android คุณสามารถใช้
AndroidSQLiteDriver
แต่สำหรับ iOS คุณใช้ NativeSQLiteDriver
ได้ ถึง
ใช้ NativeSQLiteDriver
คุณจะต้องระบุตัวเลือก Linker เพื่อให้ iOS
แอปลิงก์กับ 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")
}
}
}
ความแตกต่าง
เดิมทีห้องแชทได้รับการพัฒนาให้เป็นไลบรารี Android และต่อมาได้ย้ายข้อมูลไปยัง KMP ที่มุ่งเน้นความเข้ากันได้ของ API ห้องแชทเวอร์ชัน KMP จะแตกต่างกันบ้าง ระหว่างแพลตฟอร์มต่างๆ และจากเวอร์ชันเฉพาะสำหรับ Android ความแตกต่างเหล่านี้ แสดงไว้และอธิบายไว้ดังนี้
การบล็อกฟังก์ชัน DAO
เมื่อใช้ Room สำหรับ KMP ฟังก์ชัน DAO ทั้งหมดที่คอมไพล์สำหรับแพลตฟอร์มที่ไม่ใช่ Android
ต้องเป็นฟังก์ชัน suspend
ยกเว้นประเภทผลตอบแทนเชิงรับ เช่น
ด้วยชื่อ 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() { // … }
}
สิทธิประโยชน์ของห้องจากไลบรารี kotlinx.coroutines
แบบไม่พร้อมกันที่มีฟีเจอร์มากมาย
ที่ Kotlin ให้บริการสำหรับหลายๆ แพลตฟอร์ม เพื่อฟังก์ชันการทำงานที่ดีที่สุด suspend
มีการบังคับใช้ฟังก์ชันสำหรับ DAO ที่คอมไพล์ในโปรเจ็กต์ KMP โดยยกเว้น
DAO เฉพาะ Android เพื่อรักษาความเข้ากันได้แบบย้อนหลังกับ
ฐานของโค้ด
ความแตกต่างของฟีเจอร์กับ KMP
ส่วนนี้จะอธิบายความแตกต่างของฟีเจอร์ต่างๆ ระหว่าง KMP กับแพลตฟอร์ม Android เวอร์ชันของห้องแชท
ฟังก์ชัน @RawQuery DAO
ฟังก์ชันที่มีคำอธิบายประกอบด้วย @RawQuery
ซึ่งคอมไพล์สำหรับแพลตฟอร์มที่ไม่ใช่ Android
จะต้องประกาศพารามิเตอร์ประเภท RoomRawQuery
แทน
SupportSQLiteQuery
@Dao
interface TodoDao {
@RawQuery
suspend fun getTodos(query RoomRawQuery): List<TodoEntity>
}
จากนั้นจะใช้ RoomRawQuery
เพื่อสร้างการค้นหาระหว่างรันไทม์ได้ ดังนี้
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)
}
การติดต่อกลับจากคำค้นหา
API ต่อไปนี้สำหรับการกำหนดค่า Callback ของคำค้นหาไม่พร้อมใช้งานร่วมกัน ดังนั้นจึงไม่พร้อมใช้งานในแพลตฟอร์มอื่นที่ไม่ใช่ Android
RoomDatabase.Builder.setQueryCallback
RoomDatabase.QueryCallback
เราตั้งใจที่จะเพิ่มการรองรับการติดต่อกลับของคำถามใน Room เวอร์ชันในอนาคต
API ที่จะกำหนดค่า RoomDatabase
ที่มี Callback สืบค้น
RoomDatabase.Builder.setQueryCallback
พร้อมกับอินเทอร์เฟซ Callback
RoomDatabase.QueryCallback
ไม่พร้อมใช้งานร่วมกัน จึงไม่พร้อมให้บริการ
ในแพลตฟอร์มอื่นๆ ที่ไม่ใช่ Android
การปิดฐานข้อมูลอัตโนมัติ
API ที่จะเปิดใช้การปิดอัตโนมัติหลังจากหมดเวลา
RoomDatabase.Builder.setAutoCloseTimeout
ใช้ได้ใน Android เท่านั้น และ
ไม่พร้อมใช้งานในแพลตฟอร์มอื่น
ฐานข้อมูลก่อนการส่งแพ็กเกจ
API ต่อไปนี้ในการสร้าง RoomDatabase
โดยใช้ฐานข้อมูลที่มีอยู่ (เช่น
ฐานข้อมูลที่จัดเตรียมไว้ล่วงหน้า) ไม่ได้มีร่วมกัน จึงไม่มีอยู่ใน
แพลตฟอร์มอื่นๆ ที่ไม่ใช่ Android API เหล่านี้ได้แก่
RoomDatabase.Builder.createFromAsset
RoomDatabase.Builder.createFromFile
RoomDatabase.Builder.createFromInputStream
RoomDatabase.PrepackagedDatabaseCallback
เราตั้งใจที่จะเพิ่มการสนับสนุนสำหรับฐานข้อมูลที่จัดเตรียมไว้ล่วงหน้าใน ห้องแชท
การเอาข้อมูลเก่าในอินสแตนซ์แบบหลายอินสแตนซ์
API สำหรับเปิดใช้การทำให้หลายอินสแตนซ์ใช้งานไม่ได้
RoomDatabase.Builder.enableMultiInstanceInvalidation
มีเฉพาะใน
Android และไม่พร้อมใช้งานในแพลตฟอร์มทั่วไปหรือแพลตฟอร์มอื่นๆ