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:
- Preferences are typed, including custom types.
- Observing for changes to the preference as a RxJava Observable instead of having to use the OnSharedPreferenceChangeListener interface.
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. |