Toothpick: A new dependency Framework for Android

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

Alin

Alin

Mobile Community Manager at Softvision
Alin started his career in Softvision in 2010 with a summer internship and has worked on various mobile Android apps, some iOS apps, as well as the backend side of a few projects. He was involved in all the parts of a mobile projects from coding, to solution proposals and also coordination of engineering teams. In the last 3 years Alin worked on Groupon project and led the largest Android development team in Softvision.
Since joining the company he was an active member of Softvision University, held various tech talks both for internal and external events. He also contributed on some open source libraries and the Softvision app uSketch. Alin is a dynamic person, an incurable optimist who never refuses a challenge, he is very passionate about technology, software design, always keeping up to speed with the latest technological trends.
Alin

Why Dependency Injection?

Dependency injection is providing the dependencies that an object needs. Instead of having your objects creating a dependency or asking a factory object to make one for them, you pass the needed dependencies into the object externally, and you rely on a framework that builds the dependency graph and handles the creation of those objects. It’s a very useful technique for testing since it allows dependencies to be mocked or stubbed out. It also improves code readability and reduces the amount of code a developer has to write.

Why Toothpick?

Toothpick was created by developers from Groupon, a long time client of Softvision. Maybe you wonder why we had to implement our own framework for this. Let’s start with a bit of history.

The Softvision team has been working with dependency injection for several years. First, we started with Roboguice but as our app grew we found that the fact that Roboguice is using reflection slowed us down a lot. We tried to migrate to the another popular alternative Dagger, that turned out to be a lot faster but the learning curve was very steep, the library is complex and bloated, and we realized that we needed to change our app a lot. So we decided to create a library that combines the best from both worlds.

This experiment emphasizes startup times, with a large number of injections.

What is Toothpick?

Toothpick is a scope tree based, runtime but reflection free implementation of JSR 330. It is pure Java, with a particular focus on Android.

Toothpick is fast and is simple to use, with less boilerplate code. Its syntax is very close to Guice. It supports named dependencies, lazy dependencies, providers, and has built-in support for custom scopes. As compared to Dagger 2, Toothpick provides more powerful testing support, allowing you to leverage DI to mock dependencies using either mockito or easymock.

How do we code using Toothpick?

Example:

Let’s say that we have a snow cone machine with the following dependencies: Ice cubes and syrup dispenser.

Without toothpick the code would looks something like this:

public class SnowConeMachine {
   private IceMachine iceMachine;
   private SyrupDispenser syrupDispenser;
   public SnowConeMachine() {
       iceMachine = new IceMachine();
       syrupDispenser = new SyrupDispenser();
   }

   public SnowCone makeSnowCone() {
       SnowCone snowCone = new SnowCone();
       snowCone.addIce(iceMachine.makeIce());
       snowCone.addSyrup(syrupDispenser.dispenseSyrup());
       return snowCone;
   }
}


With Toothpick:

public class SnowConeMachine {
   @Inject IceMachine iceMachine; 
   @Inject Lazy<SyrupDispenser> syrupDispenser;

   public SnowCone makeSnowCone() {

       SnowCone snowCone = new SnowCone();
       snowCone.addIce(iceMachine.makeIce());
       snowCone.addSyrup(syrupDispenser.get().dispenseSyrup());
       return snowCone;
   }
}

We don’t need the constructor anymore!

Dependencies can also be specified as lazy. So that object will be created only when you call .get() method

Let’s take another example and take a closer look

public class SmoothieMachine {
    @Inject IceMachine iceMachine;  

    public void doSmoothie() {
       iceMachine.makeIce();
    }
}

@Inject defines that iceMachine is a dependency of the SmoothieMachine. When you inject this class, iceMachine will be created and assigned to this reference.

 @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 created as a dependency. This is constructor injection. Also, the @Singleton annotation means that only one instance of iceMachine will be created and reused by multiple components. We will talk about Singletons and custom annotations later in this article

Scopes

As mentioned before Toothpick is a scope tree based Dependency Injection (DI) library for Java.
In Toothpick, injections and instances creation always take place within a given scope.

public class MyApplication extends Application {
   @Inject Machine machine;
   @Override
   protected void onCreate() {
      super.onCreate();
      Scope appScope = Toothpick.openScope(this);
      Toothpick.inject(this, appScope);
   }
}

First, we need to open the scope and use it to make the injections. This will simply create a machine instance and assign it to the machine field.

Bindings

Let’s say that Machine is an interface and every time we want to inject a machine we actually want to inject an actual implementation: IceMachine.

Here is what we need to do:

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);
   }
}

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. As you cannot instantiate an interface directly, we need bindings. We store the bindings inside the scopes using the modules. 

Working with Scopes

Operations on the scope tree (adding/removing children, etc.) should be performed via the Toothpick class that wraps these operations.

public class LemonActivity extends Activity {


   @Inject Machine machine;
   @Inject Context context;



   @Override
   protected void onCreate(Bundle savedInstanceState) {

      super.onCreate(savedInstanceState);
      Scope scope = Toothpick.openScopes(getApplication(), this);
      Toothpick.inject(this, scope);
   }
 }

In the above example, we linked the new created Scope (Activity scope) to the application scope.

In this case, the Application scope has a child that is the activity scope. That activity 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 because of the binding we defined above.

The scopes can inherit each other.

Scopes have a name, which can be any object. Here is the code to create the scope tree above.

Toothpick.openScopes(application, activity, fragment1);
Toothpick.openScopes(application, activity, fragment2);
Toothpick.openScopes(application, activity, fragment3);
Toothpick.openScopes(application, service);

Using the objects having a life cycle as names are intuitive. When the object starts its life cycle, create a scope Toothpick.openScope(object), and when the object dies, close its associated scope Toothpick.closeScope(object). These objects are generally not created by a DI framework but can be injected and be an entry point of a dependency injection graph.

Scope Singletons

As we saw in the previous example, IceMachine is annotated with @Singleton. When injecting that class, you instantiate a class the first time, and 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 also to have local singletons or scope singletons.

Let’s take a look at the scope singletons

public class VanillaActivity extends Activity {



   @Inject Machine machine;
   @Inject VanillaFlavour vanillaFlavour;
   @Inject Context context;

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       Scope scope = Toothpick.openScopes(getApplication(), this);
        scope.installModules(new Module() {{
 bind(VanillaFlavour.class).toInstance(new VanillaFlavour());
       }});

       Toothpick.inject(this, scope);

   }
 }

 

The above example shows how we can create a singleton that is defined only inside the Activity Scope. As a result, every time we inject a VanillaFlavour class, it will bind to the same instance (the one created inside the module).

Next, let’s see how scope singletons can be define using custom scope annotations. 

Custom Scope Annotations

Scope annotations are an alternative to defining scoped bindings, more static but more convenient.

The most famous scope annotation is probably the @javax.inject.Singleton annotation.

Let’s define a custom annotation

 @javax.inject.Scope
 @Documented
 @Retention(RUNTIME)
 interface @ActivitySingleton {}
 //a class using the custom scope annotation @ActivitySingleton
 @ActivitySingleton
 class SmoothieMachine {...}

 

A scope annotation is an annotation class that is qualified by @javax.inject.Scope, such as the @ActivitySingleton scope above.

Annotating SmoothieMachine with @ActivitySingleton means “I want a single instance of SmoothieMachine in the scope @ActivitySingleton“.

Next, we need to bind a scope to this annotation :

 //Binding a scope to a scope annotation
 scope.bindScopeAnnotationClass(ActivitySingleton.class);

As you can see in this article, Toothpick is a very powerful tool that helps you manage and reuse your dependencies.

Check the following links if you wanna find out more:

Toothpick Github: https://github.com/stephanenicolas/toothpick

Migrating from Roboguice to Toothpick:
https://medium.com/@markchristopherng/migrating-off-roboguice-3-part-1-cee0875f6620

 

 

Share This Article


Alin

Alin

Mobile Community Manager at Softvision
Alin started his career in Softvision in 2010 with a summer internship and has worked on various mobile Android apps, some iOS apps, as well as the backend side of a few projects. He was involved in all the parts of a mobile projects from coding, to solution proposals and also coordination of engineering teams. In the last 3 years Alin worked on Groupon project and led the largest Android development team in Softvision.
Since joining the company he was an active member of Softvision University, held various tech talks both for internal and external events. He also contributed on some open source libraries and the Softvision app uSketch. Alin is a dynamic person, an incurable optimist who never refuses a challenge, he is very passionate about technology, software design, always keeping up to speed with the latest technological trends.
Alin
No Comments

Post A Comment