美文网首页
Android Jetpack架构组件(十一)— DataSto

Android Jetpack架构组件(十一)— DataSto

作者: 独自闯天涯的码农 | 来源:发表于2022-08-16 09:59 被阅读0次

    一、DataStore简介

    Jetpack DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 使用 Kotlin 协程和 Flow 以异步、一致的事务方式存储数据。

    Preferences DataStore 和 Proto DataStore

    DataStore 提供两种不同的实现:Preferences DataStore 和 Proto DataStore。

    • Preferences DataStore 使用键存储和访问数据。此实现不需要预定义的架构,也不确保类型安全。
    • Proto DataStore 将数据作为自定义数据类型的实例进行存储。此实现要求您使用协议缓冲区来定义架构,但可以确保类型安全。

    二、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 规则添加到 proguard-rules.pro 文件中,以免您的字段遭到删除。

    2、使用 Preferences DataStore 存储键值对

    Preferences DataStore 实现使用DataStore和Preferences类将简单的键值对保留在磁盘上。

    1.创建 Preferences DataStore

    使用由preferencesDataStore创建的属性委托来创建 Datastore<Preferences> 实例。在您的 Kotlin 文件顶层调用该实例一次,便可在应用的所有其余部分通过此属性访问该实例。这样可以更轻松地将 DataStore 保留为单例。此外,如果您使用的是 RxJava,请使用RxPreferenceDataStoreBuilder。必需的 name 参数是 Preferences DataStore 的名称。

    RxDataStore<Preferences> dataStore =new RxPreferenceDataStoreBuilder(context, /*name=*/ "settings").build();
    
    2.从 Preferences DataStore 读取内容

    由于 Preferences DataStore 不使用预定义的架构,因此您必须使用相应的键类型函数为需要存储在 DataStore<Preferences> 实例中的每个值定义一个键。例如,如需为 int 值定义一个键,请使用intPreferencesKey()。然后,使用DataStore.data属性,通过 Flow 提供适当的存储值。

    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.将内容写入 Preferences DataStore

    Preferences DataStore 提供了一个edit() 函数,用于以事务方式更新 DataStore 中的数据。该函数的 transform 参数接受代码块,您可以在其中根据需要更新值。转换块中的所有代码均被视为单个事务。

    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 实现使用 DataStore 和协议缓冲区将类型化的对象保留在磁盘上。

    1.定义架构

    Proto DataStore 要求在 app/src/main/proto/ 目录的 proto 文件中保存预定义的架构。此架构用于定义您在 Proto DataStore 中保存的对象的类型。如需详细了解如何定义 proto 架构,请参阅protobuf 语言指南。

    syntax = "proto3";
    
    option java_package = "com.example.application";
    option java_multiple_files = true;
    
    message Settings {
      int32 example_counter = 1;
    }
    

    注意:您的存储对象的类在编译时由 proto 文件中定义的 message 生成。请务必重新构建您的项目。

    2.创建 Proto DataStore

    创建 Proto DataStore 来存储类型化对象涉及两个步骤:

    定义一个实现 Serializer<T> 的类,其中 T 是 proto 文件中定义的类型。此序列化器类会告知 DataStore 如何读取和写入您的数据类型。请务必为该序列化器添加默认值,以便在尚未创建任何文件时使用。
    使用由 dataStore 创建的属性委托来创建 DataStore<T> 的实例,其中 T 是在 proto 文件中定义的类型。在您的 Kotlin 文件顶层调用该实例一次,便可在应用的所有其余部分通过此属性委托访问该实例。filename 参数会告知 DataStore 使用哪个文件存储数据,而 serializer 参数会告知 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 读取内容

    使用 DataStore.data 显示所存储对象中相应属性的 Flow。

    Flowable<Integer> exampleCounterFlow =
      dataStore.data().map(settings -> settings.getExampleCounter());
    
    4.将内容写入 Proto DataStore

    Proto DataStore 提供了一个updateData() 函数,用于以事务方式更新存储的对象。updateData() 为您提供数据的当前状态,作为数据类型的一个实例,并在原子读-写-修改操作中以事务方式更新数据。

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

    4、在同步代码中使用 DataStore

    注意:请尽可能避免在 DataStore 数据读取时阻塞线程。阻塞界面线程可能会导致ANR或界面卡顿,而阻塞其他线程可能会导致死锁。

    DataStore 的主要优势之一是异步 API,但可能不一定始终能将周围的代码更改为异步代码。如果您使用的现有代码库采用同步磁盘 I/O,或者您的依赖项不提供异步 API,就可能出现这种情况。

    Kotlin 协程提供runBlocking()协程构建器,以帮助消除同步与异步代码之间的差异。您可以使用 runBlocking() 从 DataStore 同步读取数据。RxJava 在 Flowable 上提供阻塞方法。以下代码会阻塞发起调用的线程,直到 DataStore 返回数据:

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

    对界面线程执行同步 I/O 操作可能会导致 ANR 或界面卡顿。您可以通过从 DataStore 异步预加载数据来减少这些问题:

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

    这样,DataStore 可以异步读取数据并将其缓存在内存中。以后使用 runBlocking() 进行同步读取的速度可能会更快,或者如果初始读取已经完成,可能也可以完全避免磁盘 I/O 操作。

    参考:Android开发者网站DataStore使用

    相关文章

      网友评论

          本文标题:Android Jetpack架构组件(十一)— DataSto

          本文链接:https://www.haomeiwen.com/subject/iuyrgrtx.html