RxPreferences - Android平台的响应式SharedPreferences

77 阅读3分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

SharedPreferences是Android平台上面的轻量级机制, 来保存和恢复键值对.

SharedPreferences的API过分简化, 使得它的使用因为下面几个原因而受限:

  • 类型安全. 调用者必须知道偏好的键和类型.
  • 不原生支持自定义数据类型.
  • 不支持监听单个键的变化.

RxPreferences构建于SharedPreferences 的基础之上, 修复了这些限制, 并且通过跟RxJava集成从而走的更远.

类型安全的偏好

SharedPreferences要求调用者一直知晓标识偏好的键是什么并且追踪值的类型. 否则的话可能会发生 RuntimeException.

RxPreferences引入了Preference类型. 这是个泛化接口, 适用于任何数据类型.


/** A preference of type {@link T}. Instances can be created from {@link RxSharedPreferences}. */
public interface Preference<T> {

   ......

  /** The key for which this preference will store and retrieve values. */
  @NonNull String key();

  /** The value used if none is stored. */
  @NonNull T defaultValue();

  /**
   * Retrieve the current value for this preference. Returns {@link #defaultValue()} if no value is
   * set.
   */
  @NonNull T get();

  /**
   * Change this preference's stored value to {@code value}.
   */
  void set(@NonNull T value);

  /** Returns true if this preference has a stored value. */
  boolean isSet();

  /** Delete the stored value for this preference, if any. */
  void delete();
  
  ......
  
}

Preference提供了方法来取代在SharedPreferencesSharedPreference.Editor中等价的方法. RxSharedPreferences提供了工厂方法将SharedPreferences绑定到自身, 也提供了方法来操作基础数据类型, 比如Boolean/Enum/Float/Integer/Long/String/Set<String>.

比如:

SharedPreferences preferences = getDefaultSharedPreferences(this);
RxSharedPreferences rxPrefs = RxSharedPreferences.create(preferences);
Preference<Integer> foo = rxPrefs.getInt("foo");

foo.set(3.14f); // 将不会通过编译!

自定义数据类型

SharedPreferences并没有直接的方法保存和恢复自定义数据类型. 保存时必须手动将Object转化成String, 恢复时必须手动将String转化成Object, 这是可行的, 但很不方便.

RxPreferences引入了Converter抽象从任务类型中恢复和检索值, 并且将序列化逻辑放在单独的地方.

  /**
   * Converts instances of {@code T} to be stored and retrieved as Strings in {@link
   * SharedPreferences}.
   */
  interface Converter<T> {
    /**
     * Deserialize to an instance of {@code T}. The input is retrieved from {@link
     * SharedPreferences#getString(String, String)}.
     */
    @NonNull T deserialize(@NonNull String serialized);

    /**
     * Serialize the {@code value} to a String. The result will be used with {@link
     * SharedPreferences.Editor#putString(String, String)}.
     */
    @NonNull String serialize(@NonNull T value);
  }

并且, 我们可以使用Gson或者Moshi来实现Converter, 从而序列化自定义数据类型. 举个Gson的例子:

class GsonPreferenceConverter<T> implements Converter<T> {
  final Gson gson;
  private Class<T> clazz;

  // Constructor and exception handling omitted for brevity.

  @Override
  public T deserialize(String serialized) {
    return gson.fromJson(serialized, clazz);
  }

  @Override
  public String serialize(T value) {
    return gson.toJson(value);
  }
}

之后RxSharedPreferences.getObject(key, defaultValue, converter)会将ConverterGson集成.

和RxJava一起使用

SharedPreferences#registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener)用来监听所有键的变化. 对于特定的键, 调用者必须过滤这个键.

但是来自RxPreferencesPreference<T>RxJava集成可以直接观察单个偏好的变化.

public interface Preference<T> {

  /**
   * Observe changes to this preference. The current value or {@link #defaultValue()} will be
   * emitted on first subscribe.
   */
  @CheckResult @NonNull Observable<T> asObservable();

  /**
   * An action which stores a new value for this preference.
   */
  @CheckResult @NonNull Consumer<? super T> asConsumer();
}

所以Preference#asObservablePreference转化成Observable进行订阅. 之后当Preference的值发生变化的时候, Observer将会接到通知.

@Inject @FooPreference BooleanPreference fooPreference;

fooPreference.asObservable()
  .subscribe((enabled) -> System.out.println(enabled))

RxBinding

最后, RxPreferences也可以和RxBinding一起工作. 更多信息在

注意: RxPreferences是库的名字, 并且RxSharedPreferences才是具体的类.