Android Jetpack架構(gòu)組件(十一)— DataStore

一、DataStore簡介

Jetpack DataStore 是一種數(shù)據(jù)存儲解決方案,允許您使用協(xié)議緩沖區(qū)存儲鍵值對或類型化對象。DataStore 使用 Kotlin 協(xié)程和 Flow 以異步、一致的事務(wù)方式存儲數(shù)據(jù)。

Preferences DataStore 和 Proto DataStore

DataStore 提供兩種不同的實現(xiàn):Preferences DataStore 和 Proto DataStore。

  • Preferences DataStore 使用鍵存儲和訪問數(shù)據(jù)。此實現(xiàn)不需要預(yù)定義的架構(gòu),也不確保類型安全。
  • Proto DataStore 將數(shù)據(jù)作為自定義數(shù)據(jù)類型的實例進行存儲。此實現(xiàn)要求您使用協(xié)議緩沖區(qū)來定義架構(gòu),但可以確保類型安全。

二、DataStore使用

1、添加依賴

Preferences DataStore
    // Preferences DataStore (SharedPreferences like APIs)
    dependencies {
        implementation("androidx.datastore:datastore-preferences:1.0.0")

        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-preferences-rxjava2:1.0.0")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-preferences-rxjava3:1.0.0")
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation("androidx.datastore:datastore-preferences-core:1.0.0")
    }
    
Proto DataStore
    // Typed DataStore (Typed API surface, such as Proto)
    dependencies {
        implementation("androidx.datastore:datastore:1.0.0")

        // optional - RxJava2 support
        implementation("androidx.datastore:datastore-rxjava2:1.0.0")

        // optional - RxJava3 support
        implementation("androidx.datastore:datastore-rxjava3:1.0.0")
    }

    // Alternatively - use the following artifact without an Android dependency.
    dependencies {
        implementation("androidx.datastore:datastore-core:1.0.0")
    }
    

注意:如果您將 datastore-preferences-core 工件與 Proguard 搭配使用,就必須手動將 Proguard 規(guī)則添加到 proguard-rules.pro 文件中,以免您的字段遭到刪除。

2、使用 Preferences DataStore 存儲鍵值對

Preferences DataStore 實現(xiàn)使用DataStore和Preferences類將簡單的鍵值對保留在磁盤上。

1.創(chuàng)建 Preferences DataStore

使用由preferencesDataStore創(chuàng)建的屬性委托來創(chuàng)建 Datastore<Preferences> 實例。在您的 Kotlin 文件頂層調(diào)用該實例一次,便可在應(yīng)用的所有其余部分通過此屬性訪問該實例。這樣可以更輕松地將 DataStore 保留為單例。此外,如果您使用的是 RxJava,請使用RxPreferenceDataStoreBuilder。必需的 name 參數(shù)是 Preferences DataStore 的名稱。

RxDataStore<Preferences> dataStore =new RxPreferenceDataStoreBuilder(context, /*name=*/ "settings").build();
2.從 Preferences DataStore 讀取內(nèi)容

由于 Preferences DataStore 不使用預(yù)定義的架構(gòu),因此您必須使用相應(yīng)的鍵類型函數(shù)為需要存儲在 DataStore<Preferences> 實例中的每個值定義一個鍵。例如,如需為 int 值定義一個鍵,請使用intPreferencesKey()。然后,使用DataStore.data屬性,通過 Flow 提供適當(dāng)?shù)拇鎯χ怠?/p>

Preferences.Key<Integer> EXAMPLE_COUNTER = PreferencesKeys.int("example_counter");
Flowable<Integer> exampleCounterFlow =dataStore.data().map(prefs -> prefs.get(EXAMPLE_COUNTER));
Key的類型

Preferences.Key<Int> intPreferencesKey
Preferences.Key<Double> doublePreferencesKey
Preferences.Key<String> stringPreferencesKey
Preferences.Key<Boolean> booleanPreferencesKey
Preferences.Key<Float> floatPreferencesKey
Preferences.Key<Long> longPreferencesKey
Preferences.Key<Set<String>> stringSetPreferencesKey

3.將內(nèi)容寫入 Preferences DataStore

Preferences DataStore 提供了一個edit() 函數(shù),用于以事務(wù)方式更新 DataStore 中的數(shù)據(jù)。該函數(shù)的 transform 參數(shù)接受代碼塊,您可以在其中根據(jù)需要更新值。轉(zhuǎn)換塊中的所有代碼均被視為單個事務(wù)。

Single<Preferences> updateResult =  dataStore.updateDataAsync(prefsIn -> {
  MutablePreferences mutablePreferences = prefsIn.toMutablePreferences();
  Integer currentInt = prefsIn.get(INTEGER_KEY);
  mutablePreferences.set(INTEGER_KEY, currentInt != null ? currentInt + 1 : 1);
  return Single.just(mutablePreferences);
});

3、使用 Proto DataStore 存儲類型化的對象

Proto DataStore 實現(xiàn)使用 DataStore 和協(xié)議緩沖區(qū)將類型化的對象保留在磁盤上。

1.定義架構(gòu)

Proto DataStore 要求在 app/src/main/proto/ 目錄的 proto 文件中保存預(yù)定義的架構(gòu)。此架構(gòu)用于定義您在 Proto DataStore 中保存的對象的類型。如需詳細(xì)了解如何定義 proto 架構(gòu),請參閱protobuf 語言指南。

syntax = "proto3";

option java_package = "com.example.application";
option java_multiple_files = true;

message Settings {
  int32 example_counter = 1;
}

注意:您的存儲對象的類在編譯時由 proto 文件中定義的 message 生成。請務(wù)必重新構(gòu)建您的項目。

2.創(chuàng)建 Proto DataStore

創(chuàng)建 Proto DataStore 來存儲類型化對象涉及兩個步驟:

定義一個實現(xiàn) Serializer<T> 的類,其中 T 是 proto 文件中定義的類型。此序列化器類會告知 DataStore 如何讀取和寫入您的數(shù)據(jù)類型。請務(wù)必為該序列化器添加默認(rèn)值,以便在尚未創(chuàng)建任何文件時使用。
使用由 dataStore 創(chuàng)建的屬性委托來創(chuàng)建 DataStore<T> 的實例,其中 T 是在 proto 文件中定義的類型。在您的 Kotlin 文件頂層調(diào)用該實例一次,便可在應(yīng)用的所有其余部分通過此屬性委托訪問該實例。filename 參數(shù)會告知 DataStore 使用哪個文件存儲數(shù)據(jù),而 serializer 參數(shù)會告知 DataStore 第 1 步中定義的序列化器類的名稱。

private static class SettingsSerializer implements Serializer<Settings> {
  @Override
  public Settings getDefaultValue() {
    Settings.getDefaultInstance();
  }

  @Override
  public Settings readFrom(@NotNull InputStream input) {
    try {
      return Settings.parseFrom(input);
    } catch (exception: InvalidProtocolBufferException) {
      throw CorruptionException(“Cannot read proto.”, exception);
    }
  }

  @Override
  public void writeTo(Settings t, @NotNull OutputStream output) {
    t.writeTo(output);
  }
}

RxDataStore<Byte> dataStore =
    new RxDataStoreBuilder<Byte>(context, /* fileName= */ "settings.pb", new SettingsSerializer()).build();
3.從 Proto DataStore 讀取內(nèi)容

使用 DataStore.data 顯示所存儲對象中相應(yīng)屬性的 Flow。

Flowable<Integer> exampleCounterFlow =
  dataStore.data().map(settings -> settings.getExampleCounter());
4.將內(nèi)容寫入 Proto DataStore

Proto DataStore 提供了一個updateData() 函數(shù),用于以事務(wù)方式更新存儲的對象。updateData() 為您提供數(shù)據(jù)的當(dāng)前狀態(tài),作為數(shù)據(jù)類型的一個實例,并在原子讀-寫-修改操作中以事務(wù)方式更新數(shù)據(jù)。

Single<Settings> updateResult =
  dataStore.updateDataAsync(currentSettings ->
    Single.just(
      currentSettings.toBuilder()
        .setExampleCounter(currentSettings.getExampleCounter() + 1)
        .build()));

4、在同步代碼中使用 DataStore

注意:請盡可能避免在 DataStore 數(shù)據(jù)讀取時阻塞線程。阻塞界面線程可能會導(dǎo)致ANR或界面卡頓,而阻塞其他線程可能會導(dǎo)致死鎖。

DataStore 的主要優(yōu)勢之一是異步 API,但可能不一定始終能將周圍的代碼更改為異步代碼。如果您使用的現(xiàn)有代碼庫采用同步磁盤 I/O,或者您的依賴項不提供異步 API,就可能出現(xiàn)這種情況。

Kotlin 協(xié)程提供runBlocking()協(xié)程構(gòu)建器,以幫助消除同步與異步代碼之間的差異。您可以使用 runBlocking() 從 DataStore 同步讀取數(shù)據(jù)。RxJava 在 Flowable 上提供阻塞方法。以下代碼會阻塞發(fā)起調(diào)用的線程,直到 DataStore 返回數(shù)據(jù):

Settings settings = dataStore.data().blockingFirst();

對界面線程執(zhí)行同步 I/O 操作可能會導(dǎo)致 ANR 或界面卡頓。您可以通過從 DataStore 異步預(yù)加載數(shù)據(jù)來減少這些問題:

dataStore.data().first().subscribe();

這樣,DataStore 可以異步讀取數(shù)據(jù)并將其緩存在內(nèi)存中。以后使用 runBlocking() 進行同步讀取的速度可能會更快,或者如果初始讀取已經(jīng)完成,可能也可以完全避免磁盤 I/O 操作。

參考:Android開發(fā)者網(wǎng)站DataStore使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容