iT邦幫忙

2024 iThome 鐵人賽

DAY 20
0
Mobile Development

Jetpack Compose 從心開始系列 第 20

Jetpack Compose 從心開始 Day20 - 使用 Room

  • 分享至 

  • xImage
  •  

前言

    Room 提供了一個更高層次的抽象,讓我們可以更安全、高效地使用 SQLite 資料庫。它簡化了資料庫操作,提高了開發效率,並增強了應用程式的穩定性。

Room 的主要元件

三個 Room 元件可讓這些工作流程順暢運作。

  • Room 實體代表應用程式資料庫中的資料表,可用於更新資料表中資料列儲存的資料,也可用來建立新的資料列來插入內容。
  • Room DAO 為應用程式提供各種方法,用於在資料庫中擷取、更新、插入及刪除資料。
  • Room 資料庫類別為應用程式提供與該資料庫相關聯的 DAO 例項。

https://ithelp.ithome.com.tw/upload/images/20240930/20121643edsAZSGmu0.png

新增 Room 依附元件

//Room
implementation("androidx.room:room-runtime:${rootProject.extra["room_version"]}")
ksp("androidx.room:room-compiler:${rootProject.extra["room_version"]}")
implementation("androidx.room:room-ktx:${rootProject.extra["room_version"]}")

定義資料實體 (Entity)

  • 建立一個 Kotlin 類別: 代表你想要儲存的資料。
  • 使用注解: 使用 @Entity、@PrimaryKey、@ColumnInfo 等注解來定義表格結構和欄位。
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "items")
data class Item(
    @PrimaryKey(autoGenerate = true)
    val id: Int,
    val name: String,
    val price: Double,
    val quantity: Int
)

建立資料存取物件 (DAO)

  • 定義一個介面: 使用 @Dao 注解標記。
  • 定義方法: 使用 @Query、@Insert、@Update、@Delete 等注解來定義 SQL 操作。

https://ithelp.ithome.com.tw/upload/images/20240930/201216437NnL04rYaI.png

import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import kotlinx.coroutines.flow.Flow

@Dao
interface ItemDao {
    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insert(item: Item)

    @Update
    suspend fun update(item: Item)

    @Delete
    suspend fun delete(item: Item)

    @Query("SELECT * from items WHERE id = :id")
    fun getItem(id: Int): Flow<Item>

    @Query("SELECT * from items ORDER BY name ASC")
    fun getAllItems(): Flow<List<Item>>
}

建立資料庫

  • 定義一個抽象類別: 繼承 RoomDatabase。
  • 定義一個抽象方法: 返回 DAO 的實例。
import android.content.Context
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase

/**
* Database class with a singleton Instance object.
*/
@Database(entities = [Item::class], version = 1, exportSchema = false)
abstract class InventoryDatabase : RoomDatabase() {

    abstract fun itemDao(): ItemDao

    companion object {
        @Volatile
        private var Instance: InventoryDatabase? = null

        fun getDatabase(context: Context): InventoryDatabase {
            // if the Instance is not null, return it, otherwise create a new database instance.
            return Instance ?: synchronized(this) {
                Room.databaseBuilder(context, InventoryDatabase::class.java, "item_database")
                    .build()
                    .also { Instance = it }
            }
        }
    }
}

實作存放區

import kotlinx.coroutines.flow.Flow

class OfflineItemsRepository(private val itemDao: ItemDao) : ItemsRepository {
    override fun getAllItemsStream(): Flow<List<Item>> = itemDao.getAllItems()

    override fun getItemStream(id: Int): Flow<Item?> = itemDao.getItem(id)

    override suspend fun insertItem(item: Item) = itemDao.insert(item)

    override suspend fun deleteItem(item: Item) = itemDao.delete(item)

    override suspend fun updateItem(item: Item) = itemDao.update(item)
}

Kotlin Flows 是一種用於處理非同步資料流的工具

  • 簡潔易用: Flows 的語法簡潔,易於理解和使用。
  • 高效能: Flows 可以高效地處理大量資料,並避免阻塞主線程。
  • 可取消: Flows 可以被取消,以避免不必要的計算。
  • 可組合: Flows 可以被組合成更複雜的流,以實現各種功能。

更新 UI 狀態 viewmodel

StateFlow 和 SharedFlow

  • StateFlow: 用於表示一個可變的單一值,可以被多個消費者共享。
  • SharedFlow: 用於表示一個可變的序列,可以被多個消費者共享。
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn

val homeUiState: StateFlow<HomeUiState> =
    itemsRepository.getAllItemsStream().map { HomeUiState(it) }
        .stateIn(
            scope = viewModelScope,
            started = SharingStarted.WhileSubscribed(TIMEOUT_MILLIS),
            initialValue = HomeUiState()
        )

顯示商品目錄資料

fun HomeScreen(
    navigateToItemEntry: () -> Unit,
    navigateToItemUpdate: (Int) -> Unit,
    modifier: Modifier = Modifier,
    viewModel: HomeViewModel = viewModel(factory = AppViewModelProvider.Factory)
) {
    val scrollBehavior = TopAppBarDefaults.enterAlwaysScrollBehavior()
    val homeUiState by viewModel.homeUiState.collectAsState()
    
    Scaffold(
    ....
    
    HomeBody(
            itemList = homeUiState.itemList,
            onItemClick = navigateToItemUpdate,
            modifier = modifier.fillMaxSize(),
            contentPadding = innerPadding,
        )
        
        ....
@Composable
private fun HomeBody(
    itemList: List<Item>,
    onItemClick: (Int) -> Unit,
    modifier: Modifier = Modifier,
    contentPadding: PaddingValues = PaddingValues(0.dp),
) {
    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = modifier,
    ) {
        if (itemList.isEmpty()) {
            Text(
                text = stringResource(R.string.no_item_description),
                textAlign = TextAlign.Center,
                style = MaterialTheme.typography.titleLarge,
                modifier = Modifier.padding(contentPadding),
            )
        } else {
            InventoryList(
                itemList = itemList,
                onItemClick = { onItemClick(it.id) },
                contentPadding = contentPadding,
                modifier = Modifier.padding(horizontal = dimensionResource(id = R.dimen.padding_small))
            )
        }
    }
}

執行結果

空白db
https://ithelp.ithome.com.tw/upload/images/20240930/20121643kaaOfytw1M.png

新增db資料
https://ithelp.ithome.com.tw/upload/images/20240930/20121643EuOWcwkdww.png

顯示db資料
https://ithelp.ithome.com.tw/upload/images/20240930/20121643sacbcjHjGd.png

更新db資料
https://ithelp.ithome.com.tw/upload/images/20240930/20121643RpheLWxTCk.png

刪除db資料
https://ithelp.ithome.com.tw/upload/images/20240930/20121643oJsG0BzppG.png

參考

https://developer.android.com/courses/pathways/android-basics-compose-unit-6-pathway-2?hl=zh-tw


上一篇
Jetpack Compose 從心開始 Day19 - 使用 SQL 讀取資料庫並在其中寫入資料
下一篇
Jetpack Compose 從心開始 Day21 - 使用 DataStore 儲存
系列文
Jetpack Compose 從心開始30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言