今天主要會建立Room來做資料串接。
Room 是 Android 的資料庫 Library,為 SQLite 提供抽象層,讓開發者更輕鬆、安全地使用資料庫。它簡化了資料庫操作,並支援編譯期檢查與異步查詢,避免繁瑣的 SQL 撰寫。
Room 主要有三個元件
。Entity:定義資料表結構。
。DAO (Data Access Object):操作資料表的方法。
。Database:連接資料庫的入口,使用 Room.databaseBuilder
建立。
要在 Compose 裡面使用 Room 首先要新增依賴
libs.version.toml
roomVersion = "2.6.1"
androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "roomVersion"}
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "roomVersion"}
androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "roomVersion"}
Project build.gradle.kts
plugins {
.
.
id("com.google.devtools.ksp") version "1.9.0-1.0.12" apply false
}
app build.gradle.kts
plugins {
.
.
id("com.google.devtools.ksp")
}
dependencies {
.
implementation(libs.androidx.room.runtime)
annotationProcessor(libs.androidx.room.compiler)
implementation(libs.androidx.room.ktx)
ksp(libs.androidx.room.compiler)
.
.
}
主要是定義資料表結構,我們這邊訂一個一個 TableName 叫 subscription,然後他有四個欄位分別是Id、productName、price、cycle。
@Entity(tableName = "subscription")
data class SubscriptionEntity(
@PrimaryKey
val id: Int,
@ColumnInfo(name = "product_name")
val productName: String,
@ColumnInfo(name = "price")
val price: String,
@ColumnInfo(name = "cycle")
val cycle: String
)
DAO 用來定義資料庫操作方法,透過註解如 @Insert
、@Update
、@Query
、@Delete
簡化資料存取邏輯。以下範例中,我們設計三個方法:
getAll
:用於查詢資料庫中的完整資料列表。setData
:將新資料插入資料庫。updateData
:更新已存在的資料內容。@Dao
interface SubscriptionDao {
@Query("SELECT * FROM subscription")
suspend fun getAll(): List<SubscriptionEntity>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun setData(item: SubscriptionEntity)
@Update
suspend fun updateData(item: SubscriptionEntity)
}
Database 是用來定義資料庫的主要入口,負責建立和管理資料表以及版本控制。它會包含所有對應的 DAO 介面,讓我們透過它們來進行資料存取操作。以下的範例中我們定義了一個subscriptionDao
方法它提供SubscriptionDao
介面來讓我們操作。
@Database(entities = [SubscriptionEntity::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun subscriptionDao(): SubscriptionDao
}
上面我們把 Room 建好後接下來我們要調整我們的AppDataManagerImpl
。
首先我們要先建立Database
private var dbManager: AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, "database.db").build()
把每個方法串接我們定義的DAO
class AppDataManagerImpl(context: Application) : AppDataManager {
var list = mutableListOf<SubscriptionViewData>()
private var dbManager: AppDatabase = Room.databaseBuilder(context, AppDatabase::class.java, "database.db").build()
override suspend fun getData(): List<SubscriptionViewData> {
val dataList = dbManager.subscriptionDao().getAll()
list = dataList.map {
SubscriptionViewData(
id = it.id.toString(),
name = it.productName,
price = it.price,
cycle = it.cycle
)
}.toMutableList()
return list
}
override suspend fun setData(data: SubscriptionViewData) {
val item = SubscriptionEntity(
id = list.size,
productName = data.name,
price = data.price,
cycle = data.cycle
)
dbManager.subscriptionDao().setData(item)
}
override suspend fun updateData(data: SubscriptionViewData) {
list.find { it.id == data.id }?.let {
val item = SubscriptionEntity(
id = it.id.toInt(),
productName = data.name,
price = data.price,
cycle = data.cycle
)
dbManager.subscriptionDao().updateData(item)
}
}
}
接著我們去AppApplication中調整資料源,把原先的 AppDataManagerMock
改成 AppDataManagerImpl
。
class AppApplication : Application() {
private lateinit var appDataManager: AppDataManager
override fun onCreate() {
super.onCreate()
appDataManager = AppDataManagerImpl(this)
}
fun getAppDataManager() = appDataManager
}
在測試時發現回到首頁資料不會進行,因此補上LaunchedEffect
當進入畫面時更新資料,或許 ViewModel 跟 AppDataManager 之間改用 StateFlow
也可以解決,但目前還沒想到作法,因此就先這樣調整。
LaunchedEffect(Unit) {
viewModel.reload()
}
初始畫面
接著我們新增資料
再來我們編輯資料
以上為今天的內容。