做者 / Android 開發技術推廣工程師 Florina Muntenescu 與 Google 軟件工程師 Rohit Sathyanarayana
歡迎使用 Jetpack DataStore,這是一個通過改進的全新數據存儲解決方案,旨在替代原有的 SharedPreferences。Jetpack DataStore 基於 Kotlin 協程和 Flow 開發,並提供兩種不一樣的實現: Proto DataStore 和 Preferences DataStore。其中 Proto DataStore,能夠存儲帶有類型的對象 (使用 protocol buffers 實現);Preferences DataStore,能夠存儲鍵值對。在 DataStore 中,數據以異步的、一致的、事務性的方式進行存儲,克服了 SharedPreferences 的大部分缺點。html
在兩種實現中,除非另外特指,不然 DataStore 會將首選項存儲在文件中,而且全部的數據操做都會在 Dispatchers.IO
上執行。java
雖然 Preferences DataStore 與 Proto DataStore 均可以存儲數據,但它們的實現方法不盡相同:android
若是您有局部更新數據、參照完整性或支持大型、複雜數據集的需求,則應當考慮使用 Room 而不是 DataStore。DataStore 是小型、簡單數據集的理想選擇,它並不支持局部更新與參照完整性。git
首先添加 DataStore 依賴項。若是您使用的是 Proto DataStore,請確保您也添加了 proto 依賴項:github
def dataStoreVersion = "1.0.0-alpha05" // 在 Android 開發者網站上確認最新的版本號 // https://developer.android.google.cn/jetpack/androidx/releases/datastore // Preferences DataStore implementation "androidx.datastore:datastore-preferences:$dataStoreVersion" // Proto DataStore implementation "androidx.datastore:datastore-core:$dataStoreVersion"
當您使用 Proto DataStore 時,您須要在 app/src/main/proto/ 目錄下使用 proto 文件定義您本身的 schema。有關定義 proto schema 的更多信息,請參閱 protobuf 語言指南。安全
syntax = "proto3"; option java_package = "<your package name here>"; option java_multiple_files = true; message Settings { int my_counter = 1; }
您能夠使用 Context.createDataStore()
擴展方法建立 DataStore:app
// 建立 Preferences DataStore val dataStore: DataStore<Preferences> = context.createDataStore( name = "settings" )
若是您使用的是 Proto DataStore,您還須要實現 Serializer 接口來告訴 DataStore 如何讀取和寫入您的數據類型。異步
object SettingsSerializer : Serializer<Settings> { override fun readFrom(input: InputStream): Settings { try { return Settings.parseFrom(input) } catch (exception: InvalidProtocolBufferException) { throw CorruptionException("Cannot read proto.", exception) } } override fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output) } // 建立 Proto DataStore val settingsDataStore: DataStore<Settings> = context.createDataStore( fileName = "settings.pb", serializer = SettingsSerializer )
不管是 Preferences 對象仍是您在 proto schema 中定義的對象,DataStore 都會以 Flow 的形式暴露已存儲的數據。DataStore 能夠確保在 Dispatchers.IO 上檢索數據,所以不會阻塞您的 UI 線程。ide
使用 Preferences DataStore:函數
val MY_COUNTER = preferencesKey<Int>("my_counter") val myCounterFlow: Flow<Int> = dataStore.data .map { currentPreferences -> // 不一樣於 Proto DataStore,這裏不保證類型安全。 currentPreferences[MY_COUNTER] ?: 0 }
使用 Proto DataStore:
val myCounterFlow: Flow<Int> = settingsDataStore.data .map { settings -> // myCounter 屬性由您的 proto schema 生成! settings.myCounter }
爲了寫入數據,DataStore 提供了一個 DataStore.updateData()
掛起函數,它會將當前存儲數據的狀態做爲參數提供給您,對於 Preferences
對象或是您在 proto schema 中定義的對象實例皆爲如此。updateData()
函數使用原子的讀、寫、修改操做並以事務的方式更新數據。當數據在磁盤上完成存儲時,此協程就會完成。
Preferences DataStore 還提供了一個 DataStore.edit()
函數來方便數據的更新。在此函數中,您會收到一個用於編輯的 MutablePreferences
對象,而不是 Preferences 對象。該函數與 updateData()
同樣,會在轉換代碼塊完成以後將修改應用到磁盤,而且當數據在磁盤上完成存儲時,此協程就會完成。
使用 Preferences DataStore:
suspend fun incrementCounter() { dataStore.edit { settings -> // 能夠安全地增長咱們的計數器,而不會由於資源競爭而丟失數據。 val currentCounterValue = settings[MY_COUNTER] ?: 0 settings[MY_COUNTER] = currentCounterValue + 1 } }
使用 Proto DataStore:
suspend fun incrementCounter() { settingsDataStore.updateData { currentSettings -> // 能夠安全地增長咱們的計數器,而不會由於資源競爭而丟失數據。 currentSettings.toBuilder() .setMyCounter(currentSettings.myCounter + 1) .build() } }
要從 SharedPreferences 遷移至 DataStore,您須要將 SharedPreferencesMigration 對象傳遞給 DataStore 構造器,DataStore 能夠自動完成從 SharedPreferences 遷移至 DataStore 的工做。遷移會在 DataStore 中發生任何數據訪問以前運行,這意味着在 DataStore.data 返回任何值以及 DataStore.updateData() 能夠更新數據以前,您的遷移必須已經成功。
若是您要遷移至 Preferences DataStore,您能夠使用 SharedPreferencesMigration
的默認實現。只須要傳入 SharedPreferences 構造時所使用的名字就能夠了。
使用 Preferences DataStore:
val dataStore: DataStore<Preferences> = context.createDataStore( name = "settings", migrations = listOf(SharedPreferencesMigration(context, "settings_preferences")) )
當須要遷移至 Proto DataStore 時,您必須實現一個映射函數,用來定義如何將 SharedPreferences 所使用的鍵值對遷移到您所定義的 DataStore schema。
使用 Proto DataStore:
val settingsDataStore: DataStore<Settings> = context.createDataStore( produceFile = { File(context.filesDir, "settings.preferences_pb") }, serializer = SettingsSerializer, migrations = listOf( SharedPreferencesMigration( context, "settings_preferences" ) { sharedPrefs: SharedPreferencesView, currentData: UserPreferences -> // 在這裏將 sharedPrefs 映射至您的類型。 } ) )
SharedPreferences 有着許多缺陷: 看起來能夠在 UI 線程安全調用的同步 API 其實並不安全、沒有提示錯誤的機制、缺乏事務 API 等等。DataStore 是 SharedPreferences 的替代方案,它解決了 Shared Preferences 的絕大部分問題。DataStore 包含使用 Kotlin 協程和 Flow 實現的徹底異步 API,能夠處理數據遷移、保證數據一致性,而且能夠處理數據損壞。
因爲 DataStore 仍處於測試階段,所以咱們須要您的幫助以使其變得更好!首先,您能夠經過咱們的 文檔 瞭解有關 DataStore 的更多信息,也能夠經過咱們爲您準備的兩個 Codelab: Preferences DataStore codelab 和 Proto DataStore codelab 來嘗試 DataStore。最後,您能夠在 問題跟蹤器 上建立問題,讓咱們知道如何來改進 DataStore。