### Proto DataStore 簡介 - DataStore 是比 SharedPreferences 更現代且安全的資料儲存方式。 - 分為兩種:Preferences DataStore(儲存 key-value)、Proto DataStore(儲存整個型別物件)。 - Proto DataStore 的優點:型別安全、不會回傳 null、可儲存完整的 Kotlin 資料類別(data class)。 --- ### 本教學目標與範例功能 - 示範如何使用 Proto DataStore 搭配 Kotlin Serialization。 - 範例功能為語言選擇器,儲存語言設定並在重新啟動 App 後保留設定。 - UI 簡單,但教學會涵蓋更進階的資料儲存方式。 --- ### 所需相依套件與設定 ![image](https://hackmd.io/_uploads/Sk81XMFp1e.png) - `datastore`:使用 DataStore 所需的主要套件。 - `kotlinx-collections-immutable`:若需儲存 List 時使用。 - `kotlinx-serialization`:進行物件序列化,必須在 `build.gradle` 中加入對應的 plugin。 - 所有依賴與 classpath 可在影片說明或 GitHub 找到。 --- ### 建立設定資料類別(AppSettings) - 使用 Kotlin `data class` 定義整體設定,Proto DataStore 可直接儲存整個類別。 - 比起 SharedPreferences,不需為每個欄位撰寫儲存/讀取邏輯。 - 可模組化建立多個設定類別(如:使用者設定、應用程式設定等)。 --- ### 定義儲存型別(語言與位置) - 建立 `enum class` 表示語言(英文、德文、西班牙文)。 - 建立 `data class` 表示地點(含經緯度)。 - 在 `AppSettings` 中可包含 enum 及其他 data class 型別欄位。 --- ### 讓類別支援序列化 - 所有會被儲存的類別(包括 enum 與 data class)皆需標註 `@Serializable`。 - 未標註會導致編譯錯誤,因為序列化器無法處理非標註型別。 --- ### 處理 List 資料與 Immutable 問題 - 雖然使用 `val` 定義 list,但仍可能被轉型成 mutable list 而被修改。 - 建議使用 Kotlin 提供的 `PersistentList`(不可變列表)以確保不被修改。 - 使用 `persistentListOf()` 建立真正不可變的清單,避免非預期錯誤。 --- ### 小結示意(設定中僅儲存語言) ![image](https://hackmd.io/_uploads/BJG-zMKakx.png) - 雖有介紹儲存地點清單的方式,但實際範例中僅儲存語言。 - 主要目的是教學 Proto DataStore 搭配 Kotlin Serialization 的基礎應用方式。 當然可以,以下是針對影片第二部分內容的重點整理,使用段落與條列方式呈現: --- ### 建立 Serializer 以用於 Proto DataStore ![image](https://hackmd.io/_uploads/Bk3VGzta1g.png) ![image](https://hackmd.io/_uploads/ByaSzMYpyg.png) - 必須實作 `Serializer<T>` 介面來指定如何讀取與寫入 AppSettings。 - 使用 `object` 宣告單例 serializer,避免多個實例。 - `defaultValue` 回傳預設 AppSettings 實例。 - `readFrom`:從 `InputStream` 中解析資料,使用 Kotlin Serialization 的 `Json.decodeFromString`。 - 發生序列化錯誤時回傳預設值。 - `writeTo`:將物件序列化成 JSON 字串並寫入 `OutputStream`。 - 使用 `@OptIn(ExperimentalSerializationApi::class)` 移除 Android Studio 的警告提示。 --- ### 建立 DataStore 實例 - 建議使用 `Context` 的 extension property 建立 DataStore 實例。 - 使用 `by dataStore` 指定檔名(例如:`app_settings.json`)與對應的 serializer。 - 可於 `Activity` 或 Composable 中取得此 DataStore 實例。 --- ### 寫入資料至 DataStore - 使用 `updateData` 方法修改 DataStore 中的資料。 - 回傳新的 `AppSettings` 物件以更新狀態。 - 透過 `copy()` 方法修改特定欄位(如語言)。 - 若要修改 List(如 knownLocations),使用 `.mutate {}` 操作 `PersistentList`。 --- ### 讀取資料並轉為 Compose State - DataStore 的資料為 `Flow`,可使用 `collectAsState()` 觀察變化。 - 提供預設值作為初始狀態。 - 資料變動時 UI 自動更新,優於 SharedPreferences 的靜態資料。 --- ### UI:建立語言選擇器介面 - 使用 `Column` 垂直排列三個語言選項。 - 每個語言選項為 `Row`,包含 `RadioButton` 與對應 `Text`。 - 使用迴圈產生三個 `RadioButton`,搭配 `Language.values()`。 - 判斷 `selected` 條件為當前語言是否等於 `AppSettings` 中儲存的語言。 --- ### 處理點擊事件更新語言 - 點擊 `RadioButton` 時啟動 coroutine,呼叫 `setLanguage()`。 - 需使用 `rememberCoroutineScope()` 取得 Compose 的 coroutine 範圍。 - 每次選擇新語言即更新 DataStore 並觸發 UI 重新組成。 --- ### 實作成果與持久化效果 - App 預設語言為 English,選擇後重新開啟 App 可保留設定。 - 所有設定儲存在 Android 裝置中的 JSON 檔案中。 - 透過 Proto DataStore 達成型別安全、可擴充且反應式的偏好設定儲存方案。 # Terminology - **Proto DataStore**:一種型別安全的資料儲存機制,可儲存整個 Kotlin 物件,避免 SharedPreferences 的缺點。 - **Preferences DataStore**:DataStore 的另一種形式,使用鍵值對存儲資料,類似 SharedPreferences。 - **SharedPreferences**:Android 傳統的鍵值對資料儲存方式,已逐漸被 DataStore 取代。 - **Kotlin Data Class**:一種簡潔的類別定義方式,常用於表示資料模型,自動生成 equals、hashCode、toString 等函式。 - **Serialization**:將物件轉換為可儲存或傳輸格式的過程,Kotlin 使用 kotlinx.serialization。 - **kotlinx.serialization**:Kotlin 官方的序列化庫,支援多種格式如 JSON、ProtoBuf 等。 - **@Serializable**:Kotlin 注解,用來標示類別可以被序列化與反序列化。 - **Enum Class**:列舉類別,用於定義一組固定常數值,具備型別安全性。 - **Mutable List**:可變列表,允許新增、刪除、修改元素。 - **Immutable List**:不可變列表,內容無法變動,提高安全性與穩定性。 - **PersistentList**:由 Kotlinx Collections Immutable 提供的不可變列表,支援函數式資料操作。 - **ProtoBuf**:Google 提出的高效能序列化格式,原為 Proto DataStore 的預設格式。 - **JSON**:一種輕量級資料交換格式,常用於資料儲存與網路傳輸。 - **Plugin**:Gradle 擴充功能,用於支援 Kotlin Serialization、DataStore 等功能。 - **Gradle**:Android 專案的建構系統,管理依賴與建置流程。 - **Build.gradle (project)**:全域建構設定檔,用於指定插件 classpath。 - **Build.gradle (module)**:模組級設定檔,用於加入依賴與套件。 - **Dependency Injection (DI)**:設計模式,減少模組間的耦合,常與 Hilt、Dagger 使用。 - **Jetpack**:Google 推出的 Android 架構元件集合,包括 DataStore、ViewModel 等。 - **Boilerplate Code**:重複、樣板化的程式碼,應盡量避免。 - **App Settings**:應用程式偏好設定,用於儲存使用者或系統偏好。 - **User Preferences**:特定使用者的個人偏好資料。 - **Type-Safety**:型別安全,避免型別錯誤與 NullPointerException。 - **Nullable Value**:允許為 null 的變數或值,在 Kotlin 中需特別處理。 - **Non-Nullable**:不允許為 null 的變數,有助於提高程式穩定性。 - **Kotlin Collections Immutable**:Kotlin 的不可變集合套件。 - **DataStore Serializer**:自訂資料序列化與反序列化的介面。 - **Context DataStore**:利用 Kotlin 擴充屬性將 DataStore 與 Context 綁定。 - **DataStoreScope**:協助管理 DataStore 內部協程作用範圍。 - **Coroutines**:Kotlin 的非同步程式設計工具,常與 DataStore 搭配使用。 - **Flow**:Kotlin 中用於資料流處理的類別,支援異步與反應式編程。 - **StateFlow**:一種特殊的 Flow,用於 UI 狀態管理。 - **DataStore.updateData**:更新 Proto DataStore 資料的方法。 - **DataStore.data**:用來讀取 DataStore 內容的 Flow。 - **Default Values**:預設值,用於資料未初始化時給予初始狀態。 - **Field Accessor**:存取欄位值的方式,在 Protobuf 與 Kotlin 中皆常見。 - **Immutable Data Structure**:不可變資料結構,避免不預期的狀態變更。 - **Serialization Plugin**:Gradle 外掛,用於支援 Kotlin 序列化功能。 - **ViewModel**:用於管理 UI 邏輯與狀態,通常搭配 DataStore 使用。 - **Kotlin Extension Function**:擴充函數,可擴展現有類別的功能。 - **Google Protobuf Plugin**:支援 ProtoBuf 語言的 Gradle 插件。 - **Kotlin DSL**:用 Kotlin 語法撰寫的 Gradle 腳本,比 Groovy 更具可讀性。 - **PreferencesSerializer**:自訂偏好值的序列化方式。 - **Data Migration**:舊資料轉移到新結構的過程,DataStore 支援內建遷移工具。 - **IO Dispatcher**:Kotlin 協程中的一種 Dispatcher,適用於磁碟或網路 IO 操作。 - **Main Dispatcher**:協程在主執行緒中執行,用於更新 UI。 - **Kotlin Enum Serialization**:將 Enum 類別轉換為文字格式與反向解析。 - **List Serialization**:將清單物件序列化成 JSON 或其他格式。 - **Atomic Operation**:不可中斷的操作,用於保證資料一致性。 - **Data Consistency**:資料的一致性與正確性,Proto DataStore 可透過原子操作保證。 - **Thread-Safe**:多執行緒下仍能正確執行,不會產生競爭狀況。 - **Cold Flow**:直到被收集時才會開始執行的 Flow。 - **Hot Flow**:不需被收集即可主動發送資料的 Flow,如 StateFlow。 - **Snapshot State**:Jetpack Compose 中的 UI 狀態管理工具,可與 DataStore 結合。 - **Serializer**:負責將資料物件轉換成可儲存格式(如 JSON)及從儲存格式解析為物件的元件。 - **InputStream**:Java 類別,用於讀取資料流,在序列化時讀取儲存的設定檔。 - **OutputStream**:Java 類別,用於寫入資料流,在序列化時儲存設定。 - **Default Value**:在資料損毀或無資料可讀時使用的預設值。 - **Try-Catch Block**:錯誤處理結構,用於捕捉例外如序列化錯誤。 - **SerializationException**:資料序列化過程中出現錯誤時拋出的例外。 - **decodeFromString**:將字串(如 JSON)轉換成 Kotlin 物件的函式。 - **encodeToString**:將 Kotlin 物件轉換成字串(如 JSON)的函式。 - **readBytes**:從 InputStream 讀取所有位元資料的函式。 - **decodeToString**:將位元資料轉換為字串。 - **encodeToByteArray**:將字串轉為位元組陣列以供寫入。 - **@OptIn**:Kotlin 注解,用來啟用實驗性或需要額外同意的 API。 - **Context Extension**:對 Context 類別的擴充屬性,方便取得 DataStore 實例。 - **by dataStore**:使用屬性委託建立 DataStore 實例的方式。 - **DataStore File Name**:DataStore 在內部儲存的檔案名稱。 - **updateData**:DataStore 提供的函式,用於修改並儲存設定資料。 - **copy()**:Kotlin data class 提供的函式,用於複製並修改物件內容。 - **mutate()**:PersistentList 的操作方式,回傳可變版本以進行變更。 - **Composable Function**:Jetpack Compose 中用來定義 UI 元件的函式。 - **collectAsState()**:將 Flow 轉換為 Compose 的 State,以便在 UI 中觀察變化。 - **rememberCoroutineScope()**:在 Compose 中創建並記憶 CoroutineScope。 - **CoroutineScope**:Kotlin 協程作用範圍,用於管理非同步任務。 - **suspend function**:可暫停並恢復執行的函式,用於協程中執行非同步邏輯。 - **Column**:Compose 中的直向排列容器。 - **Row**:Compose 中的橫向排列容器。 - **RadioButton**:可選項目元件,通常用於表示選項清單。 - **Modifier.fillMaxSize()**:設定元件填滿螢幕的修飾符。 - **Modifier.align()**:設定元件對齊方式的修飾符。 - **Arrangement.Center**:設定排列方向為置中的屬性。 - **Alignment.CenterVertically**:設定垂直方向置中的屬性。 - **for loop (in 0..2)**:使用 Kotlin 的範圍運算符建立簡單循環。 - **Enum.values()**:取得 Enum 所有常數的陣列。 - **setLanguage(language)**:自定義函式,用於更新語言設定至 DataStore。 - **Text()**:Compose 顯示文字的元件。 - **Spacer**:Compose 中的間距元件,用來控制元件間距。 - **8.dp**:Compose 使用 dp 單位定義大小。 - **State<AppSettings>**:儲存型別為 AppSettings 的 Compose 狀態。 - **Flow<AppSettings>**:由 DataStore 提供的資料流,可觀察設定變化。 - **Data Persistence**:資料持久化,確保資料關閉應用後仍被保留。 - **Reactive UI**:UI 根據資料變化自動更新的設計模式。 - **Type-Safe Serialization**:確保資料型別一致的序列化方式。 - **Jetpack Compose**:Google 推出的聲明式 UI 架構。 - **Coroutine Dispatchers**:協程排程器,決定任務在哪個執行緒執行。 - **UI Thread**:主要執行緒,負責顯示與操作使用者介面。 - **MainActivity**:Android 應用的主要進入點 Activity。 - **Kotlin Plugin**:支援 Kotlin 特性如序列化的編譯器外掛。 - **build.gradle.kts**:Kotlin DSL 格式的 Gradle 設定檔。 - **Json Object (from kotlinx.serialization)**:用於進行序列化與反序列化的 Json 處理器。 - **Immutable Configuration**:不允許在程式執行中更改的設定,有助於防止錯誤。 - **Debug Logging**:除錯用的輸出訊息,通常以 `printStackTrace` 或 `Log` 實現。 - **Exception Handling**:處理執行期間可能出現的錯誤或異常情況的機制。 - **Composable State Management**:使用 Compose 的狀態管理來自動更新 UI。 - **Functional Programming**:一種以不可變性與純函式為主的程式設計範式,DataStore 設計理念之一。 - **ViewModelScope**:ViewModel 專用的 CoroutineScope,用於處理業務邏輯與狀態儲存。