Android Core

RxPreferences and Dagger

I have been rewriting a Settings (Preferences) activity for an old Android app. Since I was using RxJava in the app, I decided to try out RxPreferences which allows you to use SharedPreferences with a reactive wrapper.

I found it quite good to use, although the blog post that accompanies it is a bit out of date. While I did’t use RxBinding with it, it still had these advantages:

Inject RxPreferences with Dagger

Getting the preferences to where they are needed can be done with Dependency Injection with libraries such as Dagger 2. For instance, this is an example of a Dagger module that provides application scope dependencies with RxSharedPreferences setup:

@Module
class AppModule {

  // the application context required to get the shared preferences
  @Singleton
  @Provides
  Context provideContext(Application application) {
    return application.getApplicationContext();
  }

  @Provides
  @Singleton
  SharedPreferences provideSharedPreferences(Context context) {
    return PreferenceManager.getDefaultSharedPreferences(context);
  }

  @Provides
  @Singleton
  RxSharedPreferences provideRxSharedPreferences(SharedPreferences sharedPreferences) {
    return RxSharedPreferences.create(sharedPreferences);
  }
}

Then inject RxSharedPreferences into the classes that will use it.

public class MyActivity extends AppCompatActivity {

  // RxSharedPreferences injected into a field for an activity
  @Inject
  RxSharedPreferences rxSharedPreferences;

  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    // I'm using the Dagger Android library to inject dependencies
    AndroidInjection.inject(this);

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);
  }

  private void someMethod()
  {
    <Preference<String> myPreference =   rxSharedPreferences.getString("pref_key");
    String stringFromPreference = myPreference.get();

    // do something with the string obtained from the preference
  }
}

Inject Preferences directly

Alternatively you can inject the Preference(s) rather than RxSharedPreferences, by setting up the Preference in the Dagger module.

class AppModule {

  @Singleton
  @Provides
  Context provideContext(Application application) {
    return application.getApplicationContext();
  }

  @Provides
  @Singleton
  SharedPreferences provideSharedPreferences(Context context) {
    return PreferenceManager.getDefaultSharedPreferences(context);
  }

  @Provides
  @Singleton
  RxSharedPreferences provideRxSharedPreferences(SharedPreferences sharedPreferences) {
    return RxSharedPreferences.create(sharedPreferences);
  }

  // Preference configured with hard-coded preference key
  @Provides
  @Named("myPreference")
  @Singleton
  Preference<String> provideMyPreference(RxSharedPreferences rxPreferences) {
    return rxPreferences.getString("my_preference_key");
  }

  // Preference configured with preference key retrieved from string resource
  @Provides
  @Named("myPreferenceUsingResource")
  @Singleton
  Preference<String> provideMyPreferenceUsingResource(Context context, RxSharedPreferences rxPreferences) {
    return rxPreferences.getString(context.getString(R.string.pref_my_preference_key));
  }
}

Notice that, as an example, I have configured 2 Preference’s in the module, one with the preference key hard-coded and the other with the preference key retrieved from string resources.

Also I have added the @Named anotation, which in this case is to distinguish the two Preferences since they are both of the type String. Even if I didn’t have multiple Preferences of the same type, I think it is a good idea to qualify them for better documentation (see the Dagger documentation to learn about qualifiers.

Then inject the Preference(s) instead of RxSharedPreferences.

public class MyActivity extends AppCompatActivity {

  @Inject
  @Named("myPreference")
  Preference<String> myPreference;

  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    AndroidInjection.inject(this);

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);
  }

  private void someMethod()
  {
    String stringFromPreference = myPreference.get();

    // do something with the string obtained from the preference
  }
}

Then we can also use the injected Preference to observe changes to the it.

public class MyActivity extends AppCompatActivity {

  @Inject
  @Named("myPreference")
  Preference<String> myPreference;

  private Disposable disposable;
 
  @Override
  public void onCreate(Bundle savedInstanceState)
  {
    AndroidInjection.inject(this);

    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);
 
    disposable = myPreference.asObservable()
      .subscribe(pref -> doSomething(pref));
  }
}

Custom Preference Types

In the blog for the RxPreferences library, it demonstrates getting a Preference for a custom type by creating an Adapter class for it. In the current version, this has been replaced with a class that implements the Preference.Converter interface instead.

Here is a simple example that converts a preference stored as a String into a Preference<Boolean>. Firstly create the Converter class:

public final class BooleanStringConverter implements Preference.Converter<Boolean> {

  @NonNull
  @Override
  public Boolean deserialize(@NonNull String serialized) {
    return Boolean.parseBoolean(serialized);
  }

  @NonNull
  @Override
  public String serialize(@NonNull Boolean value) {
    return String.valueOf(value);
  }
}

Then to get the Preference, use getObject() with the Converter class as a parameter. In this example I have added it to the Dagger module.

@Provides
@Singleton
BooleanStringConverter provideBooleanStringConverter() {
  return new BooleanStringConverter();
}

@Provides
@Named("myBooleanPreference")
@Singleton
Preference<Boolean> provideMyBooleanPreference(RxSharedPreferences rxPreferences, BooleanStringConverter booleanStringConverter) {
  return rxPreferences.getObject("my_preference_key", FALSE, booleanStringConverter);
}

Using the Preference with the Converter, as well as Dagger, meant I didn’t have type conversion code scattered throughout the app.

Published on Java Code Geeks with permission by David Wong, partner at our JCG program. See the original article here: RxPreferences and Dagger

Opinions expressed by Java Code Geeks contributors are their own.

David Wong

David is a software developer who has worked in the UK, Japan and Australia. He likes building apps in Java, web and Android technologies.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button