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.

Android Studio 3.0 – Initial Impressions of Tool Support

I have been using Android Studio 3.0 since since the alpha versions, and it is good to see it finally released.

This is just some initial comments on using some common and new Android tools and libraries with the 3.0 version of Android Studio and the associated Android Gradle plugin.

Hopefully this will be useful for anyone thinking of upgrading from Android Studio 2.x.

Java 8 Finally, Maybe

Java 8 was released back in 2014, and with Android Studio 3.0 it is finally supported in Android. Finally I can say goodbye to RetroLambda (although many thanks to the authors for this very useful library).

Just be aware that many of the Java 8 language features will only be available if the minSkdVersion is 24.

Since devices running Nougat or Oreo are still in the minority, supporting older versions will continue to be an issue for a while if you want to use Java 8. Some ideas floated around to support older API version, are to have min API version flavors or  do Build.VERSION checks for the API version in the code to provide alternative code paths.

Desugar

The Jack tool chain has been replaced with the desugar tranformation process to support Java 8. Some older libraries and code may have some problems with this build stage.

JUnit 5

JUnit 5 has also been released recently, and with the help of this plugin you can use it for unit testing in Android Studio.

However if you use JUnit 4 Rules in your tests, there is limited support in JUnit 5 as they are meant to be replaced with extensions. Some Rules may be supported with the JUnit 5 migration support, but if not you will have to wait until the Rules are rewritten as extensions or not use the Rules at all.

Of course this is only for unit testing. Instrumentation tests still depend on JUnit 4 and the AndroidJUnitRunner.

Update (7 Dec 2017)

The authors of the JUnit5 plugin have stated that they now have support for JUnit 5 in instrumentation tests. I won’t be using it for a while since it requires the minSkdVersion to be 26. If anyone has tried this, please let me know how well it works.

Architecture Components, RxJava

I have also been using some of Google’s Architecture Components, but only the ViewModel and LiveData. This is another alternative to design patterns such as MVP or MVVM with Databinding.

If you use RxJava, then this will of course continue to work as it is compatible with Java 6. You may consider replacing RxJava with the more light weight LiveData for activities and fragments, and they can be adapted to each other.

Dependency Injection with Dagger

I have had no problems with Google’s Dagger, but be aware of the API changes for the Android Gradle plugin 3.0 in the gradle build file (e.g. implemention instead of compile, annotationProcessor instead of apt, etc).

See the Android Gradle plugin 3.0 migration guide.

Butterknife, Timber

Some common utilities like Butterknife and Timber for logging are working fine. I’m also using the slf4j binding for Timber without any trouble.

JRebel for Android

During the Android Studio alpha and beta process, JRebel for Android was always playing catch up with the latest version. I still have issues with the current version (2.4.14) of their free edition, so I am using instant run instead at the moment.

I’m a big fan of JRebel so I’m hoping they will have a stable version of their android plugin for Android Studio 3.0 soon.

Update (31 Jan 2018)

JRebel for Android is no longer being actively developed.

AOP with AspectJ

There are various AspectJ plugins for Android,  and the one I have been using in Android Studio 2.x was this one. Unfortunately it doesn’t support the Android plugin for Gradle 3.0 yet, but the author seem to be working on it so hopefully the support will be there soon.

Update (15 Nov 2017)

Version 3.2.0 of the GradleAspectJ-Android plugin now supports the Android plugin for Gradle 3.0. I like this plugin because it supports both aj files and annotation style aspects.

Things will get better

Of course since Android Studio 3.0 has only recently got general release, we should expect tool and library support to improve going forward.

I have only scratched the surface with some common Android tools and libraries, and there are many more around. Please let me know of your experiences with others.

Best RxJava Book, so far

These days I tend to read blogs to catch up on the latest programming techniques, although I still read the occasional book. But every now and then I come across a book that is absolutely brilliant, one such as Reactive Programming with RxJava.

This is a great book for learning about RxJava as it goes way beyond just repeating the API and documentation. As well as the usual marble diagrams, the authors help you to take a peek under the hood to explain things like:

  • Use cases about the why and when to use various RxJava constructs.
  • Design decisions about why RxJava does things in certain ways.
  • Comparison with non-Rx and alternative techniques to achieve asynchronous processing, e.g. imperative programming, Futures, manual threading, etc.
  • Integration with existing legacy code and 3rd party libraries.
  • The importance of flatMap operator for asynchronous processing as well as for flattening out nested Observables.

This just scratches the surface in terms of what this book offers, but these were particularly informative for me as a beginner/intermediate RxJava programmer.

Caveat

The reason I put ‘so far’ in the title of this blog is that book documents RxJava 1,  and the downside is that RxJava 2 was released soon after the book was published.

Having said that, most of the contents of the book are still applicable, but you have to mentally translate the concepts and examples to the RxJava 2 API. Hopefully the authors will update the book to RxJava 2 in a future edition.