Sunday, June 30, 2019

Introducing Inject-Java

I wrote a dependency injection framework. You can check it out at https://github.com/fuelyourdev/inject-java. And without further ado, here's a copy/paste of the README file on it. (Hey, I already wrote one article on it in the form of a README. No point in writing a completely separate article just to put on the blog.)

Inject-Java

A reflection based dependency injection framework for Java that doesn't use annotations.

Motivation

There are a number of dependency injection frameworks on the market, and numerous available for Java. From the Spring framework that has dependency injection as just one of it's many tools, to the standalone frameworks Guice and Dagger 2, there's already a lot to choose from. So why did I build yet another dependency injection framework?

Well, honestly part of the reason was simply to learn and improve as a developer. But there was also the more practical reason in that each of the aforementioned frameworks require annotating the classes before the framework will take those classes and compose them into an object graph, and to be honest I don't much care for that. Annotating each class is in a small way making each class aware of the dependency injection framework. I like the idea of keeping all the configuration in the composition root, and keeping the classes unaware of the dependency injection container.

So I've built Inject-Java, a dependency injection container that doesn't require that classes be annotated, but rather will strive to intelligently build your object graph, while allowing you to specify rules to clarify situations that it is unable to figure out on its own.

Installation

I haven't as of yet uploaded to Maven Central (it's on my todo list and I'll come back to it eventually), but it's simple enough to build from it's source. It's a standard Gradle setup, and the source code has no dependencies on other libraries. The test code only depends on Junit and Hamcrest.

Documentation

Container

The Container class is the main class that you'll work with. It will compose your object graph following rules that you specify while trying to be intelligent about what you don't specify, in order to minimize the amount of configuration code that you need to write.

Container.buildContainer()

So to start out, the Container class has a static method called buildContainer(). Use this to get the dependency injection container.
import dev.fuelyour.injectjava.Container;

public class Main {
  public static void main(String[] args) {
    Container container = Container.buildContainer();
  }
}
Note that by doing a static import of the buildcontainer method, you can remove the "Container." at the beginning of the call, which can allow for a shorter, cleaner syntax.
import dev.fuelyour.injectjava.Container;
import static dev.fuelyour.injectjava.Container.buildContainer;

public class Main {
  public static void main(String[] args) {
    Container container = buildContainer();
  }
}

container.get(Class<T> clazz)

Next we'll want to take a look at the get(Class<T> clazz) method. Calling this method (or the related get(String name) method, which we'll go over later) is what will generally kick off the building of your object graph.

It will use Java Reflection to build and return an instance of the class specified.
public class Root { }

public class Main {
  public static void main(String[] args) {
    Container container = buildContainer();
    Root root = container.get(Root.class);
  }
}
In addition, it will build and supply any dependencies in the constructor, so long as it can determine how to build them.
public class Root {

  private final Dep1 d1;
  private final Dep2 d2;

  public Ex2Root(Dep1 d1, Dep2 d2) {
    this.d1 = d1;
    this.d2 = d2;
  }

  public Dep1 getD1() {
    return d1;
  }

  public Dep2 getD2() {
    return d2;
  }
}

public class Dep1 { }

public class Dep2 { }

public class Main {
  public static void main(String[] args) {
    Container container = buildContainer();
    Root root = container.get(Root.class);
  }
}
And it will do the same for any dependencies of the dependencies, and so on.
public class Root {

  private final Dep1 d1;
  private final Dep2 d2;

  public Root(Dep1 d1, Dep2 d2) {
    this.d1 = d1;
    this.d2 = d2;
  }

  public Dep1 getD1() {
    return d1;
  }

  public Dep2 getD2() {
    return d2;
  }
}

public class Dep1 {

  private final Dep3 d3;

  public Dep1(Dep3 d3) {
    this.d3 = d3;
  }

  public Dep3 getD3() {
    return d3;
  }
}

public class Dep2 {

  private final Dep4 d4;

  public Dep2(Dep4 d4) {
    this.d4 = d4;
  }

  public Dep4 getD4() {
    return d4;
  }
}

public class Dep3 { }

public class Dep4 {

  private final Dep5 d5;

  public Dep4(Dep5 d5) {
    this.d5 = d5;
  }

  public Dep5 getD5() {
    return d5;
  }
}

public class Dep5 {
}

public class Main {
  public static void main(String[] args) {
    Container container = buildContainer();
    Root root = container.get(Root.class);
  }
}
It will construct each class as a singleton, and will pass the same single instance into all places that request it.
public class Root {

  private final Dep1 d1;
  private final Dep2 d2;

  public Root(Dep1 d1, Dep2 d2) {
    this.d1 = d1;
    this.d2 = d2;
  }

  public Dep1 getD1() {
    return d1;
  }

  public Dep2 getD2() {
    return d2;
  }
}

public class Dep1 {

  private final Dep2 d2;
  private final Dep3 d3;

  public Dep1(Dep2 d2, Dep3 d3) {
    this.d2 = d2;
    this.d3 = d3;
  }

  public Dep2 getD2() {
    return d2;
  }

  public Dep3 getD3() {
    return d3;
  }
}

public class Dep2 {

  private final Dep3 d3;

  public Dep2(Dep3 d3) {
    this.d3 = d3;
  }

  public Dep3 getD3() {
    return d3;
  }
}

public class Dep3 { }

public class Test {
  
  @Test
  void willCreateASingleInstanceOfEachClass() {
    Container container = buildContainer();
    Root root = container.get(Root.class);
    Dep1 d1 = container.get(Dep1.class);
    Dep2 d2 = container.get(Dep2.class);
    Dep3 d3 = container.get(Dep3.class);

    assertThat(d1, is(root.getD1()));
    assertThat(d2, is(root.getD2()));
    assertThat(d2, is(d1.getD2()));
    assertThat(d3, is(d1.getD3()));
    assertThat(d3, is(d2.getD3()));
  }
}

container.apply(Module module)

Now there are a number of cases where it can't automatically figure out the right way to build something. Take, for instance, a class with multiple constructors. It has no way of knowing which constructor you intend for it to use, and will throw an exception without guidance.
public class Root {

  private final Dep1 d1;
  private final Dep2 d2;

  public Root(Dep1 d1) {
    this.d1 = d1;
    this.d2 = null;
  }

  public Root(Dep2 d2) {
    this.d1 = null;
    this.d2 = d2;
  }

  public Dep1 getD1() {
    return d1;
  }

  public Dep2 getD2() {
    return d2;
  }
}

public class Dep1 { }

public class Dep2 {

  private final Dep3 d3;

  public Dep2() {
    this.d3 = null;
  }

  public Dep2(Dep3 d3) {
    this.d3 = d3;
  }

  public Dep3 getD3() {
    return d3;
  }
}

public class Dep3 { }

public class Main {
  public static void main(String[] args) {
    Container container = buildContainer();
    container.get(Root.class); //This will throw an exception.
  }
}
to fix this, you need to apply a module that has a rule specifying which constructor to use. (We'll go into more detail on the specifics of rules later.)
public class Main {
  public static void main(String[] args) {
    Container container = buildContainer();
    container.apply(rc -> rc.setRule(
        Root.class,
        useConstructor(Root.class,
            withParams(Dep1.class))));
    Root root = container.get(Root.class);
  }
}
By applying multiple modules, you can add the rules from each module, or override rules applied in earlier modules with rules applied in later modules.
public class Test {
  @Test
  void laterModulesCanAddRulesAndOverrideRulesFromEarlierModules() {
    Container container = buildContainer();

    container.apply(rc -> rc.setRule(
        Root.class,
        useConstructor(Root.class,
            withParams(Dep1.class))));

    container.apply(rc -> rc
        .setRule(
            Root.class,
            useConstructor(Root.class,
                withParams(Dep2.class)))
        .setRule(
            Dep2.class,
            useConstructor(Dep2.class,
                withParams(Dep3.class))));

    Root root = container.get(Root.class);

    assertThat(root, is(notNullValue()));
    assertThat(root.getD1(), is(nullValue()));
    assertThat(root.getD2(), is(notNullValue()));
    assertThat(root.getD2().getD3(), is(notNullValue()));
  }
}
This can be very useful for integration tests where you want to test everything together, but want to swap out a couple of classes for testing purposes.
public class Root {
  private final Dep1 d1;
  private final Dep2 d2;

  public Root(Dep1 d1) {
    this.d1 = d1;
    this.d2 = null;
  }

  public Root(Dep2 d2) {
    this.d1 = null;
    this.d2 = d2;
  }

  public Dep1 getD1() {
    return d1;
  }

  public Dep2 getD2() {
    return d2;
  }
}

public class Dep1 { }

public class Dep2 { }

public class Main {

  private static Root root;

  public static void main(String[] args) {
    programMain(args, null);
  }

  public static void programMain(String[] args, Module testModule) {
    Container container = buildContainer();
    container.apply(rc -> rc.setRule(Root.class,
        useConstructor(Root.class,
            withParams(Dep1.class))));
    if (testModule != null) {
      container.apply(testModule);
    }
    root = container.get(Root.class);
  }

  public static Root getRoot() {
    return root;
  }
}

public class Test {
  @Test
  void integrationTestProgramMainExample() {
    Main.main(new String[]{});
    Root root = Main.getRoot();
    
    assertThat(root.getD1(), is(notNullValue()));
    assertThat(root.getD2(), is(nullValue()));
    
    Main.programMain(new String[]{},
        rc -> rc.setRule(Root.class,
            useConstructor(Root.class,
                withParams(Dep2.class))));
    root = Main.getRoot();
    
    assertThat(root, is(notNullValue()));
    assertThat(root.getD1(), is(nullValue()));
    assertThat(root.getD2(), is(notNullValue()));
  }
}
public class Root {
  private final Dep1 d1;
  private final Dep2 d2;

  public Root(Dep1 d1) {
    this.d1 = d1;
    this.d2 = null;
  }

  public Root(Dep2 d2) {
    this.d1 = null;
    this.d2 = d2;
  }

  public Dep1 getD1() {
    return d1;
  }

  public Dep2 getD2() {
    return d2;
  }
}

public class Dep1 { }

public class Dep2 { }

public class Main {

  private static Root root;

  public static Root getRoot() {
    return root;
  }

  public static void main(String[] args) {
    Container container = buildContainer();
    container.apply(rc -> rc.setRule(Root.class,
        useConstructor(Root.class,
            withParams(Dep1.class))));
    if (args.length > 0) {
      String moduleClassName = args[0];
      applyTestModule(container, moduleClassName);
    }
    root = container.get(Root.class);
  }

  @SuppressWarnings("unchecked")
  private static void applyTestModule(
      Container container,
      String moduleClassName) {
    try {
      Class clazz =
          (Class) Class.forName(moduleClassName);
      Constructor ctor = clazz.getConstructor();
      Module testModule = ctor.newInstance();
      container.apply(testModule);
    } catch (Exception ignored) { }
  }
}

public class TestModule implements Module {

  @Override
  public void configure(RuleConfig rc) {
    rc.setRule(Root.class,
              useConstructor(Root.class,
                  withParams(Dep2.class)));
  }
}

public class Test {
  @Test
  void integrationTestArgExample() {
    Main.main(new String[]{});
    Root root = Main.getRoot();
    assertThat(root, is(notNullValue()));
    assertThat(root.getD1(), is(notNullValue()));
    assertThat(root.getD2(), is(nullValue()));

    Main.main(new String[]{TestModule.class.getName()});
    root = Main.getRoot();
    assertThat(root.getD1(), is(nullValue()));
    assertThat(root.getD2(), is(notNullValue()));
  }
}

container.get(String name)

There are times when you will need to inject instances of the same class with different values. To allow for these situations, the instances can be identified by a String name value instead of the class. The container has a get(String name) method to allow you to access these directly.
public class Test {
  @Test
  void canAccessNamedInstancesOfAClass() {
    Container container = buildContainer();
    container.apply(rc -> rc
        .setRule(String.class, useInstance("default"))
        .setRule("first", useInstance("one"))
        .setRule("second", useInstance("two")));

    assertThat(container.get(String.class), is("default"));
    assertThat(container.get("first"), is("one"));
    assertThat(container.get("second"), is("two"));
  }
}
Note that a named value won't be returned when the class is specified, but you can set a rule to associate a named value with the class, or vice versa, essentially allowing you to pick a default for when a name isn't present.
public class Test {
  @Test
  void aNameCanBeAssociatedWithAClass() {
    Container container = buildContainer();
    container.apply(rc -> rc
        .setRule(String.class, useInstance("one"))
        .setRule("first", get(String.class))
        .setRule("second", useInstance("two")));

    assertThat(container.get(String.class), is("one"));
    assertThat(container.get("first"), is("one"));
    assertThat(container.get("second"), is("two"));
  }

  @Test
  void aClassCanBeAssociatedWithAName() {
    Container container = buildContainer();
    container.apply(rc -> rc
        .setRule("first", useInstance("one"))
        .setRule(String.class, get("first"))
        .setRule("second", useInstance("two")));

    assertThat(container.get(String.class), is("one"));
    assertThat(container.get("first"), is("one"));
    assertThat(container.get("second"), is("two"));
  }
}
You can even associate two names to the same instance. Be careful in your use of this. While I can imagine use cases, I also see it as a way of shooting yourself in the foot if it gets out of control.
public class Test {
  @Test
  void twoNamesCanBeAssociatedToTheSameInstance() {
    Container container = buildContainer();
    container.apply(rc -> rc
        .setRule("first", useInstance("One instance"))
        .setRule("second", get("first")));

    assertThat(container.get("first"), is("One instance"));
    assertThat(container.get("second"), is("One instance"));
  }
}
To inject named values into classes that the container builds, you will need to associate the names with the corresponding constructor parameters.
public class Root {

  private final Dep1 d1;
  private final Dep2 d2;

  public Root(Dep1 d1, Dep2 d2) {
    this.d1 = d1;
    this.d2 = d2;
  }

  public Dep1 getD1() {
    return d1;
  }

  public Dep2 getD2() {
    return d2;
  }
}

public class Dep1 {

  private final String value;

  public Dep1(String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }
}

public class Dep2 {

  private final String value;

  public Dep2(String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }
}

public class Test {
  @Test
  void associateNameWithTheCorrespondingConstructorParameters() {
    Container container = buildContainer();
    container.apply(rc -> rc
        .setRule("first", useInstance("one"))
        .setRule("second", useInstance("two"))
        .setRule(Dep1.class,
            useSoleConstructor(Dep1.class,
                withParamNames(paramName("first", String.class))))
        .setRule(Dep2.class,
            useSoleConstructor(Dep2.class,
                withParamNames(paramName("second", String.class)))));
    Root root = container.get(Root.class);

    assertThat(root.getD1().getValue(), is("one"));
    assertThat(root.getD2().getValue(), is("two"));
  }
}
The parameter name is associated with the parameter class in the constructor. If the constructor has multiple parameters of the same class, then it will assume the first occurrence, unless otherwise specified by you.
public class Root {

  private final Dep firstDep;
  private final Dep secondDep;

  public Root(Dep firstDep, Dep secondDep) {
    this.firstDep = firstDep;
    this.secondDep = secondDep;
  }

  public Dep getFirstDep() {
    return firstDep;
  }

  public Dep getSecondDep() {
    return secondDep;
  }
}

public class Dep {

  private final String value;

  public Dep(String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }
}

public class Test {
  @Test
  void specifyParamNameOccurrenceWhenMultipleParamsWithSameClass() {
    Container container = buildContainer();
    container.apply(rc -> rc
        .setRule("first", useInstance("one"))
        .setRule("second", useInstance("two"))
        .setRule("firstDep",
            useSoleConstructor(Dep.class,
                withParamNames(paramName("first", String.class))))
        .setRule("secondDep",
            useSoleConstructor(Dep.class,
                withParamNames(paramName("second", String.class))))
        .setRule(Root.class,
            useSoleConstructor(Root.class,
                withParamNames(
                    paramName("firstDep", Dep.class),
                    paramName("secondDep", Dep.class, 2)))));

    Root root = container.get(Root.class);
    Dep firstDep = root.getFirstDep();
    Dep secondDep = root.getSecondDep();

    assertThat(firstDep, is(not(secondDep)));
    assertThat(firstDep.getValue(), is("one"));
    assertThat(secondDep.getValue(), is("two"));
  }
}

RuleConfig

ruleConfig.setRule(...)

There are four variants of the setRule method, but they essentially do the same thing. Each will take two parameters: a key that will tell the container what this rule will be applied to, and a lambda that is the rule to apply. The variants essentially allow you to choose whether the rule is keyed to a class or to a name, and choose whether or not you need access to the SimpleContainer supplied to your lambda.

The SimpleContainer supplies various access methods for getting other objects stored in the container, specifying parameter names, and specifying what class or constructor to use.
public class Root {

  private final String value;

  public Root() {
    this.value = null;
  }

  public Root(String value) {
    this.value = value;
  }

  public String getValue() {
    return value;
  }
}

public class Test {
  @Test
  void setRuleClassSupplier() {
    Container container = buildContainer();
    container.apply(rc -> rc.setRule(String.class, () -> "Hello World"));
    String greeting = container.get(String.class);

    assertThat(greeting, is("Hello World"));
  }

  @Test
  void setRuleClassFunction() {
    Container container = buildContainer();
    container.apply(rc -> rc.setRule(
        Root.class,
        c -> c.buildWithConstructor(Root.class, new Class[]{})));
    Root root = container.get(Root.class);

    assertThat(root.getValue(), is(nullValue()));
  }

  @Test
  void setRuleNameSupplier() {
    Container container = buildContainer();
    container.apply(rc -> rc.setRule("greeting", () -> "Hello World"));
    String greeting = container.get("greeting");

    assertThat(greeting, is("Hello World"));
  }

  @Test
  void setRuleNameFunction() {
    Container container = buildContainer();
    container.apply(rc -> rc.setRule(
        "root",
        c -> c.buildWithConstructor(Root.class, new Class[]{})));
    Root root = container.get("root");

    assertThat(root.getValue(), is(nullValue()));
  }
}
Commonly used rules have helper methods built out for them in the Rules class.
public class Root {

  private final Dep dependency;

  public Root() {
    this.dependency = null;
  }

  public Root(Dep dependency) {
    this.dependency = dependency;
  }

  public Dep getDependency() {
    return dependency;
  }
}

public class Dep {

  private final String firstName;
  private final String lastName;

  public Dep(String firstName, String lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }

  public String getFirstName() {
    return firstName;
  }

  public String getLastName() {
    return lastName;
  }
}

public class Test {
  @Test
  void ruleHelpers() {
    Container container = buildContainer();
    container.apply(rc -> rc
        .setRule(String.class, useInstance("Bob"))
        .setRule("firstName", get(String.class))
        .setRule("lastName", useInstance("Smith"))
        .setRule(Dep.class,
            useSoleConstructor(Dep.class,
                withParamNames(
                    paramName("firstName", String.class),
                    paramName("lastName", String.class, 2))))
        .setRule(Root.class,
            useConstructor(Root.class,
                withParams(Dep.class))));
    Root root = container.get(Root.class);

    assertThat(root.getDependency(), is(notNullValue()));
    assertThat(root.getDependency().getFirstName(), is("Bob"));
    assertThat(root.getDependency().getLastName(), is("Smith"));
  }
}

ruleConfig.setGeneralRule(...)

There are many times where the rules that you'd need to implement are very similar to each other, and could be better handled with some general purpose rule, and that's the exact point of the GeneralRule. You essentially write two lambda expressions, one that serves as the condition that determines whether or not the rule should be applied, and the other is the rule itself. I've provided pre built rules for a couple of common scenarios.

The first scenario is the situation where a class has multiple constructors, one of which is a default constructor with no parameters. This rule will default to using the default constructor when no more specific rule is given.
public class Root {

  private final String value;

  public Root() {
    value = null;
  }

  public Root(String value) {
    this.value = value;
  }
  
  public String getValue() {
    return value;
  }
}

public class Test {
  @Test
  void defaultConstructorGeneralRule() {
    Container container = buildContainer();
    container.apply(rc -> rc.setGeneralRule(useDefaultConstructor()));
    Root root = container.get(Root.class);
    
    assertThat(multi.getClassWithNoDeps(), is(nullValue()));
  }
}
Another scenario is for if you choose to follow the design pattern where all classes have interfaces. This frequently leads to a one to one mapping and a naming convention of the class having the same name as the interface, but with Impl on the end. This general rule will match up all such classes with their interfaces via the naming convention.
public interface Interface {
  Dep getDep();
}

public class InterfaceImpl implements Interface {
  
  private final Dep dep;
  
  public InterfaceImpl(Dep dep) {
    this.dep = dep;
  }
  
  @Override
  public Dep getDep() {
    return dep;
  }
}

public class Dep { }

public class Test {
  @Test
  void generalRuleTest() {
    Container container = buildContainer();
    container.apply(rc -> rc.setGeneralRule(useInterfaceImplRule()));
    Interface i = container.get(Interface.class);
    
    assertThat(i, is(instanceOf(InterfaceImpl.class)));
  }
}
You can have multiple general rules, and they will be evaluated in the order that they are added to the container. The first one that matches the condition will be used to construct the object. Note that rules are checked before general rules, so as such a rule will override a general rule. Also, if no general rule is present with a matching condition, it will then move on to the general purpose constructor.

Also note that a rule name can optionally be supplied for a general rule. The purpose of this rule name is to give a way to override the general rule in a followup module. If a general rule with the same name as a previous general rule is applied, it will overwrite the previous general rule. Also note that it will take the same spot in the evaluation line that the previous general rule held. So this is the only way that a general rule added later can be placed in front of general rules that were added before it.

ruleConfig.setTransformRule(...) and ruleConfig.setGeneralTransformRule(...)

In addition to constructor injection, most other dependency injection frameworks also support method injection and field injection, and while most experts on the subject recommend sticking with constructor injection, there are plenty of legitimate cases for these other types of injection (or at least field injection, more on that in a moment). So how does inject-java handle this? It does so through transform rules. Transform rules are rules that are applied to the object after it has been constructed and registered with the container. This is done be the object instance being supplied to the lambda you write for the rule.

Now like the rule and the general rule discussed prior, the transform rule allows you to apply a rule specified by class or name, and the general transform rule allows you to specify a condition under which the rule is applied. But unlike with the rule and general rule, the transform rule actually just a general transform rule where the condition is built out for you. As such, both the transform rule and the general transform rule are more like the general rule, in that you can have as many of them as you want, and you can specify a rule name for later overriding. But they differ from the general rule in that all rules will be applied that have matching conditions, and not just the first matching condition.

It's also worth noting that the transform rules and general transform rules live in the same collection, so a transform rule can be overridden by a general transform rule, and vice versa.

An example of how this can be used is for dealing with circular references (note that in general you want to avoid having circular references, but if you need to have a circular reference, this is how you deal with it). If class A needs a reference to class B and class B needs a reference to class A, then pure constructor injection is out of the question. You'll instead need to have at least one of the classes utilize method injection. Something kind of like this:
public class Root {
  
  private final Dep dep;
  
  public Root(Dep dep) {
    this.dep = dep;
  }
}

public class Dep {
  
  private Root root;
  
  public Dep() { }
  
  public void setRoot(Root root) {
    this.root = root;
  }
}

public class Main {
  public static void main(String[] args) {
    Container container = buildContainer();
    container.apply(rc -> rc.setTransformRule(
        Dep.class,
        (c, dep) -> dep.setRoot(c.get(Root.class))));
    Root root = container.get(Root.class);
  }
}
Now I don't have specific example code for the general transform rule at the moment, but a powerful use case would be to register methods in a class as handlers of events based off of annotations on the method or class, or based off the name of the class or method. Ultimately, there's a lot of potential power in general rules and general transform rules.

License

Wednesday, June 19, 2019

Learning RxJava: Thoughts on Chapter 12

Kotlin

Kotlin can work extremely well with RxJava. In general, Kotlin is a cleaner and simpler language than Java, but is still compatible with Java code and Java libraries. Though it should be noted that there can sometimes be some compatibility issues with certain Java libraries and SAM ambiguity. JetBrains is aware of this issue and is looking into a fix.

In the meantime, there is a helper library called RxKotlin that works around these issues with the RxJava library, in addition to giving a handful of tools.

One big plus with using Kotlin is that is supports extension functions. This in turn allows for a much cleaner implementation of custom operators, since they can be called directly from the previous operator instead of having to call compose() or lift() and passing in the custom operator.

There has been talk in the Kotlin community about implementing a pure Kotlin Rx library. With a quick look around the internet, this looks like it could be promising, though it's still a work in progress: https://github.com/badoo/Reaktive

Learning RxJava: Thoughts on Chapter 11

RxJava on Android

In short, Rx is extremely popular on mobile platforms, with RxJava being heavily used on Android and RxSwift being used on iOS.

One of the pain points, though, is that a large number of Android devices are still stuck with Java 6, which is missing lambda functions. The same thing can still be accomplished with anonymous classes, but they are extremely verbose. To fix this issue, you can use Retrolambda, which will allow you to use Java 8 style lambdas, but will then compile it to use anonymous classes in the byte code. Another option is just to forego Java altogether, and use Kotlin instead, which many developers have opted to do.

Two important libraries to include with android development are RxAndroid and RxBinding. The RxAndroid library has a handful of useful tools, primary of which are the Android Schedulers. With this you can switch between the main thread for UI elements and other threads for processing. RxBinding is an Rx wrapper for UI elements. There are also many other libraries available that can be useful depending on your use case. Go to https://github.com/ReactiveX/RxAndroid/wiki to see a list of these libraries.

Learning RxJava: Thoughts on Chapter 10

Testing and Debugging

Blocking Subscribers and Operators


There are blocking subscribers and operators that can be used to ensure the test waits until you get your results from the chain so that you can run your tests against them. While these can be useful in tests, frequently the TestObserver and TestSubscriber are better choices. Note that while it can be tempting to use blocking subscribers and operators in production code, you should avoid doing so, because it limits the effectiveness and flexibility of your reactive code.

TestObserver and TestSubscriber


TestObserver (for Observables) and TestSubscriber (for Flowables) make for very powerful and flexible tools for writing unit tests. They will keep track of data related to emissions, errors, and completion events, which allows you to examine and assert this data afterwards. They even have assertion methods built into them, such as assertValueCount, assertValues, etc.

TestScheduler


The TestScheduler is a special scheduler that allows you to manipulate time for testing purposes. It has methods such as advanceTimeBy and advanceTimeTo that will allow you to test time bound code quickly. The book does note that the TestScheduler  "is not a thread-safe Scheduler and should not be used with actual concurrency." If the code that you wish to test doesn't allow easy access to setting the Scheduler, you can instead use RxJavaPlugins.setComputationScheduler() or other such methods that will override the standard Schedulers and inject the TestScheduler in its place.

Debugging Strategies


Debugging Rx code can be difficult, since frequently the stack traces can be less than useful and it can sometimes be difficult to apply breakpoints where you need them, but a strategy that can work well is to take advantage of the doOnNext operator and other similar operators to be able to see what's going on at every step of the chain and to track down where the faulty link in the chain is located.

Tuesday, June 11, 2019

Learning RxJava: Thoughts on Chapter 9

compose()

You can create custom operators through composing existing operators by implementing the ObservableTransformer interface, and then passing it in to the compose() operator. This is useful for extracting duplicated code.

Often, creating a static method that will return the ObservableTransformer will allow for a clean and easy way to pass it into the compose operator.

This can also be done for Flowables using the FlowableTransformer.

to()

The to() operator simply takes a Function<Observable<T>, R>, which can be used to fluently convert the Observable to something else. This can be quite useful for mapping from an Observable to a language or framework specific construct such as a Future.

lift()

Sometimes there arises the need to build a custom operator from scratch, instead of composing it from existing operators. In such a situation you can implement the ObservableOperator interface and pass the resulting object to the lift() operator.

Note that this should be a rare occurrence, and it can be quite difficult to get right. Try your best to first implement your needs using the ObservableTransformer and the compose() operator, but if you determine that this is insufficient, then take a close look at how the standard operators are implemented in RxJava source code as well as trusted operator libraries such as RxJava2-Extras.

Learning RxJava: Thoughts on Chapter 8

Backpressure and Flowables

Backpressure is only needed in multi-threading situations with a large number of emissions. When the rx call chain is all executed on one thread, the thread will take each emission one at a time all the way through the chain from the source observable to the final observer. When the chain is executed across multiple threads, then the first thread will take an emission through the chain to the hand off point and hand it off to the next thread, and then immediately get the next emission and move it through.

If the second thread is slower at moving emissions through its part of the call chain, then the number of emissions that the first thread has finished but the second thread has yet to start can pile up. If the total number of emissions is small, then this isn't really an issue, but with a large number of emissions this pile up could potentially lead to an out of memory error. This is where backpressure and Flowables come in.

Essentially, using a Flowable in this example is telling thread one to slow down. It would do this by having thread one process a certain number of emissions at the start, and then wait until thread two has pulled through a certain percentage of those emissions before processing more. In this way, it tries to keep a small buffer of emissions between the two threads without letting it get out of control, thus keeping thread 2 busy without pause, but without overloading memory with thread one going nonstop.

Note that Flowable does add overhead, so opt for Observable in cases where backpressure isn't needed, but when it is needed, simply switching Observable to Flowable should be sufficient in most cases. Though there are a handful of exceptions.

Since dealing with these exceptions is infrequent, I don't feel the need to go into detail here, but if you do run into such a situation, there are a number of options available, depending on the situation, varying from using an onBackpressureXXX() operator to creating your own custom Flowable.

Wednesday, June 5, 2019

Learning RxJava: Thoughts on Chapter 7

Buffering

The buffer() operator will gather emissions into collections according to a specified criteria. The default collection type is as a list, but a different collection type can be specified.

You can specify a count, and the buffer operator will group emissions into lists with a size equal to the count, with the exception of the last list, which will contain the remainder of what couldn't be divided equally.

You can additionally specify a skip amount as well, which determines how far to move forward for the start of each list. If no skip is given it defaults to be the same as the count operator so as to break them into distinct groups. But if, for instance, you specify a count of 2 and a skip of 1, then each list will have 2 elements, but the start of a list will only be one more than the start of the previous list, which will cause the last element of the previous list the first element of the current list to be the same. This can be very useful for operations where you need to know both the current emission and the previous emission to do something.

You can also buffer based off of time, so that all emissions within a specified time are grouped together. There is also a timeSkip option, which is the time based equivalent of skip. There is also an optional count operation, so that it will group based off of whichever is reached first, the time or the count.

Additionally, buffer can take another Observable as a parameter, and anytime that Observable emits serves as the cutoff point for grouping.

Windowing

Windowing works the exact same as buffering, but instead of grouping the emissions into collections, the emissions are grouped into Observables. This can be useful in that it will allow you to work with the emissions coming in immediately, instead of waiting for the last one to be available before you can look at any of them.

Throttling

Throttling will throw away emissions when they are coming too fast. Variants include throttleLast(), throttleFirst(), and throttleWithTimeout(). throttleLast()/sample() will only emit the last item from a fixed time interval. throttleFirst() will only emit the first item from a fixed time interval. throttleWithTimeout()/debounce() will wait until there is a pause of a specified length, and then it will send the last emission from before the pause. The downside is that emissions are delayed until the end of the specified time period.

Switching

switchMap() is similar to flatMap, in that it maps an emission to an observable. The difference between them being that while flatMap will combine the emissions from all Observables into a single Observable emiting all emissions, switchMap will unsubscribe from an Observable as soon as an emission comes in and creates a new Observable. So at any given point, it is only passing on the emissions of one Observable, and that one Observable is the one created by the most recently received emission. This can be useful in cancelling and restarting expensive operations that are kicked off by user events.

Learning RxJava: Thoughts on Chapter 6


Concurrency and Parallelization

Simply put, in order to utilize the full power and speed of the CPU, you need to be able to run things concurrently. Concurrency (also called multithreading) is essentially multitasking, or performing more than one thing at the same time. The modern CPU has multiple cores, and the only way to utilize multiple cores is through multithreading. Without multithreading, you are limited to a single core.

There are a number of gotchas around concurrency, which RxJava tries to smooth out and make simple. For instance, there's a fair amount of overhead in creating a thread, so a best practice in multithreading is instead to have a thread pool, where threads are previously created, and when a thread is needed, it is pulled out of this pool, and when it is no longer needed, it is returned to this pool instead of being destroyed. RxJava strives to make this process simple through the use of Schedulers.

Schedulers

Schedulers are essentially predefined thread pool managers, with there being multiple different managers for the different types of tasks to be performed.

Computation

The computation scheduler is designed around computation heavy tasks; the kind of tasks that would require the full use of the CPU core. These tasks usually focus around math, algorithms, or complex logic. To handle this, the computation scheduler limits the number of threads in its thread pool based off of the processor count available to the JVM.

IO

The io scheduler is generally used for waiting on devices or protocols that are slow, where a fair amount of what the thread needs to do is sit around and wait for a response. This is commonly the case with disk read operations or calls over a network. To handle these, the io scheduler will try to match the number of threads in it's pool to the number of tasks that are needing a thread. To do this, it will dynamically grow or shrink its thread pool.

New Thread

The new thread scheduler is straightforward enough. It will create a new thread for each Observer, and then destroy the thread when it is done. There is no thread pool. This is useful for the every once in a while situation where it makes sense to have an individual thread for a very specific purpose.

Single

The single scheduler has a single thread in its pool. This can be useful "to isolate fragile, non-threadsafe operations to a single thread."

Trampoline

"In practicality, you will not invoke [the trampoline scheduler] often as it is used primarily in RxJava's internal implementation. [...] It is just like the default scheduler on the immediate thread, but it prevents cases of recursive scheduling where a task schedules a task while on the same thread. Instead of causing a stack overflow error, it will allow the current task to finish and then execute that new scheduled task afterward."

ExecutorService

You can build a custom scheduler off of an ExecutorService. This will allow you to have fine grained control over the thread pool and the rules used to govern it. This is useful for those circumstances where the defaults don't fit your needs.

Starting and Stopping Schedulers

Each of the default schedulers is lazily instantiated. At any point in time you can call shutdown() on any of them to immediately stop its threads, or call Schedulers.shutdown() to stop them all at once. After that, you can call start() to start any of them back up, or Schedulers.start() to start them all back up at once.

Using Schedulers

To use schedulers, you will utilize the subscribeOn(), observeOn(), and unsubscribeOn() methods.

subscribeOn()

The subscribeOn() method is used to suggest which scheduler should be used to begin the Observable chain. The placement of the subscribeOn() method in the chain has no effect, since in each case it will suggest that the Observable chain be started off on the specified scheduler, but in general best practice is to keep it as close to the source Observable as possible. Note that subscribeOn() will not work with certain Observable factories, such as Obsevable.interval. In such cases, these factories will have a method overload that will allow you to specify the scheduler directly to the factory.

observeOn()

The observeOn() method is used to switch to a different scheduler at that point in the Observable chain, such that the part above the observeOn() call will be run on one thread, and the part below will be run on a different thread. The observeOn() serves as a bridge, moving data from one thread to the next.

This can be particularly useful in applications that have a dedicated UI thread, allowing you to switch off of the UI thread to perform computations, then switch back to the UI thread to update the user interface. In this was you can keep the UI from freezing.

unsubscribeOn()

If unsubscribing from an observable is particularly costly, the unsubscribeOn() method can be used to specify that the unsubscribe code be run on a different thread. This can be useful in situations where, for instance, database connections need to be closed on unsubscribe.

Parallelization with flatMap()

If you have a lot of emissions that you need to send through some intensive or time consuming operations and you'd like to speed up the process by performing these operations across multiple threads, you can do so by taking advantage of flatMap(). FlatMap merges multiple Observables together, and it's designed to work even if those Observables are on different threads.

So, to run these operations in parallel, take each emission (or group of emissions) and flatMap it to an Observable that uses either subscribeOn or observeOn to move it to another thread. It's as simple as that.

Major Takeaways

One of the things that makes Rx extremely powerful is the ability to take sequential code and in just one or two lines transform it into code that runs on a separate thread or runs mutiple threads in parallel. It greatly simplifies concurrency and parallelization.