Heads up, we’ve moved! If you’d like to continue keeping up with the latest technical content from Square please visit us at our new home https://developer.squareup.com/blog

Dagger 2 is a great dependency injection library, but its sharp edges can be tricky to handle. Let’s go over a few best practices that Square follows to keep mobile engineers from hurting themselves!

Favor constructor injection over field injection

Field injection requires the fields to be non final and non private.

// BAD

class CardConverter { @Inject PublicKeyManager publicKeyManager; @Inject public CardConverter() {}

}

Forgetting an @Inject on a field introduces a NullPointerException .

// BAD

class CardConverter { @Inject PublicKeyManager publicKeyManager;

Analytics analytics; // Oops, forgot to @Inject @Inject public CardConverter() {}

}

Constructor injection is better because it allows for immutable and therefore thread safe objects that don’t have a partially constructed state.

// GOOD

class CardConverter { private final PublicKeyManager publicKeyManager; @Inject public CardConverter(PublicKeyManager publicKeyManager) {

this.publicKeyManager = publicKeyManager;

}

}

Kotlin eliminates the constructor injection boilerplate:

class CardConverter

@Inject constructor(

private val publicKeyManager: PublicKeyManager

)

We still use field injection for objects constructed by the system, such as Android activities:

public class MainActivity extends Activity { public interface Component {

void inject(MainActivity activity);

} @Inject ToastFactory toastFactory; @Override protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

Component component = SquareApplication.component(this);

component.inject(this);

}

}

Singletons should be extremely rare

Singletons are useful when we need a centralized access to a mutable state.

// GOOD

@Singleton

public class BadgeCounter { public final Observable<Integer> badgeCount; @Inject public BadgeCounter(...) {

badgeCount = ...

}

}

If an object has no mutable state, it doesn’t need to be a singleton.

// BAD, should not be a singleton!

@Singleton

class RealToastFactory implements ToastFactory {

private final Application context; @Inject public RealToastFactory(Application context) {

this.context = context;

} @Override public Toast makeText(int resId, int duration) {

return Toast.makeText(context, resId, duration);

}

}

On rare occasions, we use scoping to cache instances that are expensive to create, or that are repeatedly created and thrown away.

Favor @Inject over @Provides

@Provides methods should not duplicate the constructor boilerplate.

methods should not duplicate the constructor boilerplate. Code is easier to understand when coupled concerns are in one place.

@Module

class ToastModule {

// BAD, remove this binding and add @Inject to RealToastFactory

@Provides RealToastFactory realToastFactory(Application context) {

return new RealToastFactory(context);

}

}

This is especially important for singletons; it’s a key implementation detail that you need to know when reading that class.

// GOOD, I have all the details I need in one place.

@Singleton

public class BadgeCounter { @Inject public BadgeCounter(...) {}

}

Favor static @Provides methods

Dagger @Provides methods can be static.

@Module

class ToastModule {

@Provides

static ToastFactory toastFactory(RealToastFactory factory) {

return factory;

}

}

The generated code can directly invoke the method instead of having to create a module instance. That method call can be inlined by the compiler.

@Generated

public final class DaggerAppComponent extends AppComponent {

// ... @Override public ToastFactory toastFactory() {

return ToastModule.toastFactory(realToastFactoryProvider.get())

}

}

One static method won’t change much, but all bindings being static will result in a sizable performance increase.

Make your modules abstract and Dagger will fail at compile time if one of the @Provides methods isn’t static.

@Module

abstract class ToastModule {

@Provides

static ToastFactory toastFactory(RealToastFactory factory) {

return factory;

}

}

Favor @Binds over @Provides

@Binds replaces @Provides for when you’re mapping one type to another.

@Module

abstract class ToastModule {

@Binds

abstract ToastFactory toastFactory(RealToastFactory factory);

}

The method must be abstract. It will never be invoked; the generated code will know to directly use the implementation.

@Generated

public final class DaggerAppComponent extends AppComponent {

// ... private DaggerAppComponent() {

// ...

this.toastFactoryProvider = (Provider) realToastFactoryProvider;

} @Override public ToastFactory toastFactory() {

return toastFactoryProvider.get();

}

}

Avoid @Singleton on interface bindings

Statefulness is an implementation detail

Only implementations know if they need to ensure centralized access to mutable state.

When binding an implementation to an interface, there shouldn’t be any scoping annotation.

@Module

abstract class ToastModule {

// BAD, remove @Singleton

@Binds @Singleton

abstract ToastFactory toastFactory(RealToastFactory factory);

}

Enable error-prone

Several Square teams are using it to detect common Dagger mistakes, check it out.

Conclusion

These guiding principles work well for our context: small heterogeneous teams working on a large shared Android codebase. Since your context is likely different, you should apply what makes the most sense for your team.

It’s your turn! What good practices do you follow to keep your dagger code sharp?