Daniel molinero droidcon boston toothpick header

Toothpick: A New Approach to Dependency Injection on Android

Toothpick is a scope tree based, runtime with a special focus on Android that is in pure Java. I will introduce Toothpick, its main features and how it compares to other Dependency Injection libraries.

Introduction

My name is Daniel. I work at Groupon, and I’m the co-author of Toothpick. Here I’m going to introduce a new Dependency Injection framework, which is a fresh approach to Android, called Toothpick.

Why Dependency Injection?

Dependencies injection helps us decouple - we want to have small entities help with injection. It’s important to know how they work, and how they are implemented so that it will contribute to being able to reuse those dependencies.

Why Toothpick?

Initially, Android did not have a dependency injection framework. Then, Dagger came along as a fast solution for a DI framework, but, it’s verbose and complex.

Toothpick is simpler: you only need to define your dependencies and not the components. Toothpick is as fast as Dagger, and it has better testing support.

Toothpick Features

The following is not something new compared to Dagger:


public class SmoothieMachine {

  @Inject IceMachine iceMachine;
  
  public void doSmoothie() {
    iceMachine.makeIce();
  }
}

Get more development news like this

@inject defines that iceMachine is at dependency of the SmoothieMachine. When I inject this class, iceMachine will be created and assigned to this reference. I will also have to inject the below.


@Singleton
public class IceMachine {
  Foo foo;
  
  @Inject
  public IceMachine(Foo foo) {
    this.foo = foo;
  }
}

When iceMachine is created, it will use the above constructor, and foo will be a dependency. This is constructor injection.

Scopes

I will show you now some new concepts. First, the Scope is to make injections.


public class MyApplication extends Application {

  @Inject Machine machine;
  
  @Override
  protected void onCreate() {
    super.onCreate();
    
    Scope appScope = Toothpick.openScope(this);
    appScope.installModules(...);
    Toothpick.inject(this, appScope);
  }
}

The above Application class has one dependency, a machine. First, we need to open the scope, and I use it to make the injections. This will simply create a machine instance and assign it to the machine field.

Bindings

We can set up the ways injections are performed by the Scopes, using Bindings. In Dagger, these are the provider methods. We use this binding approach when we cannot inject our class. Let’s say that we want to inject an interface (this cannot be created by Toothpick), or if there is a class we cannot contract (an HTTP client). An interface cannot be created by Toothpick, or anyone. We store the bindings inside the scopes using the modules.


public class MyApplication extends Application {

  @Inject Machine machine;
  
  @Override
  protected void onCreate() {
    super.onCreate();
    
    Scope appScope = Toothpick.openScope(this);
    appScope.installModules(new Module() {{
      bind(Machine.class).to(IceMachine.class);
    }});
    Toothpick.inject(this, appScope);
  }
}

Scope Tree

scope-tree

The scopes can inherit each other. In this case, the Application scope has a child that is the activity scope. That activity one scope will inherit all the bindings from the application scope. If we use this activity scope to inject a machine, we will be injecting an iceMachine.

The idea is that you can use them, not to inherit the binding.


public class LemonActivity extends Activity {

  @Inject Machine machine;
  @Inject Context context;
  
  @Override
  projected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    Scope scope = Toothpick.openScopes(getApplication(), this);
    scope.installModules(new Module() {{
      bind(Context.class).toInstance(this);
    }});
    Toothpick.inject(this, scope);
  }
}

Open the Scope, then install the modules and inject.

Scope Singletons

A class is a singleton. When injecting that class, you instantiate a class the first time, then you use the same instance of the class afterward. In Toothpick, the way we do it is by keeping those instances inside the application scope. Next time you try to inject the same dependency using the application scope or any of the children scope, we will go to the application scope, give the same instance, and use it again.

It is possible to also have local singletons or scope singletons.

Singletons can be defined in modules

Suppose there is a class we want to reuse only for part of our app. If we define that element as an activity singleton, that would be enough to reuse that element inside the scope.

By using annotations, it’s possible to specify a singleton as local only. Every time we inject it, it will be reused if you used a particular scope. Once the scope is closed, the singleton will be gone.

MVP & State Preservation

With Toothpick, state preservation can be easy by using a local singleton. That presenter can be a local singleton.

The first time we inject it inside activity, we inject the presenter. When the activity’s destroyed and created again, when it gets injected, the presenter will be the same one that was created the first time.


public class MyActivity extends Activity {

@Inject MyPresenter presenter;

@Override
protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  Scope scope = openScopes(getApplication(),       
  PresenterSingleton.class, this);
  ToothPick.inject(this, scope);
  }
}

Unit Testing

In your system, you should have tests for every unit. We shouldn’t test all of them together, and we need to mock the dependencies.

Consider the Smoothie Machine class:


public class SmoothieMachine {

  @Inject IceMachine iceMachine;
  @Inject SyrupDispenser syrupDispenser;
  
  public void doSmoothie() {
    Smoothie smoothie = iceMachine.makeIce();
    syrupDispenser.addSyrup(smoothie);
    return smoothie;
   }
}

There are two dependencies, an IceMachine and a SyrupDispenser. To test the class, I use EasyMock. I add the rule to create a Mock, and annotate their dependencies with @Mock. The other step is to inject those mocks inside the class that we are testing. Because we are using Toothpick for the unit tests, we would use the constructor.

Lastly, we call toothPickRule.inject. This will inject all the mocks that we have created using EasyMock inside the class we are testing.

### Questions?

Q: Are you using all code generation or are you using any reflection? Daniel: Toothpick is as fast as Dagger because we are not using reflection. It works similar to Dagger. We are generating some factors and memory injectors and we use them at the right time.

Q: Do you have named dependencies? If you have an application context and an activity context, can you differentiate? Daniel: Yes. You can use named annotations.

Q: You have an activity that has multiple fragments and each one has a presenter and you’re using the interstitial scope and an activity gets recreated, how does that work for holding on to states of all of those? Daniel: The only thing you need to do is define different names for the presenters. In this case, I’m using these presenter singleton class.

Q: How will you do it with Espresso testing, when you want different mock objects during testing? Daniel: The issues with Espresso is more like running the real Android application. You need to open the second scope. The application scope that stays the same is the one that will be created in production for your app, but then this second scope that you create for the test will be where you inject all the mocks. This will not be easy.

Next Up: Android Architecture Components and Realm

General link arrow white

About the content

This talk was delivered live in April 2017 at Droidcon Boston. The video was recorded, produced, and transcribed by Realm, and is published here with the permission of the conference organizers.

Daniel Molinero Reguera

Android Software Engineer at Groupon. Developer with 7 years’ experience building Web and Mobile applications. Solid understanding of software design optimized for embedded systems and the full mobile development lifecycle with the Android SDK. Deep knowledge of data structures, algorithms and security. BSc and MSc in Computer Science with honors. Passionate about innovation and building world class products.

4 design patterns for a RESTless mobile integration »

close