HomeAndroidMarinator - Dependency Injection Made Delicious

Marinator – Dependency Injection Made Delicious

When using dependency injection, getting access to the classes which perform the injection is a common problem. On Android, the most common solution to this tends to be to store the components in the Application object. But this then requires the developer to reach into their Application object in multiple places throughout their code. This creates several challenges – in addition to just looking ugly, it can make it harder to write pure JUnit tests. In this journal entry, we will be having a look at Marinator Dependency Injection.

Marinator Dependency Injection made delicious

Marinator helps solve this problem by wrapping your components with a simple static class. Instead of calling code like MyApplication.get().getComponent().inject(this), you can simply call Marinator.inject(this). Marinator relies on an annotation processor to generate a helper that registers your injectors – as a developer, all you have to do is annotate your injector methods and provide the component to the MarinadeHelper.

Marinator was created with Dagger2 style dependency injection in mind. But there’s no requirement that you use Dagger2 or even any sort of framework for DI to use Marinator. As long as your injector class has a method annotated with @Injector, Marinator will recognize this and add it to the prepare method.

Setup

Marinator is distributed via Jitpack. To use Marinator in your project, add the following lines to your build.gradlefile:

// Root build.gradle file:
allprojects {
  repositories {
    maven { url "https://jitpack.io" }
  }
}

// App-level build.gradle file:

// If using Kotlin:
apply plugin: 'kotlin-kapt'

dependencies {
  compile 'com.github.blueapron.marinator:marinator:1.0.2'
  annotationProcessor 'com.github.blueapron.marinator:marinator-processor:1.0.2'

  // If using Kotlin:
  kapt 'com.github.blueapron.marinator:marinator-processor:1.0.2'
}

When you define your components, declare the injector methods and annotate them with @Injector. So, as an example:

@Singleton
@Component(modules = ApplicationModule.class)
public interface ApplicationComponent {
  @Injector
  void inject(Recipe recipe);

  // Note that the injector method can be named
  // whatever you want - it doesn't have to be
  // called "inject".
  @Injector
  void provide(Wine wine);

  // Other Dagger component dependencies declared here.
}

The next time you compile after adding this annotation, the annotation processor will generate the MarinadeHelperclass for you. Wherever you create your components, you can use the MarinadeHelper to register them as injectors:

// In your application class:
public class MyApplication extends Application {

  // ...

  @Override
  public void onCreate() {
    super.onCreate();

    // Create our components and register as an injector. Note that we can
    // use multiple components here as needed.
    mApplicationComponent = createApplicationComponent();
    mNetworkComponent = createNetworkComponent(mApplicationComponent);
    MarinadeHelper.prepare(mApplicationComponent, mNetworkComponent);
  }
}

// In unit tests:
public abstract class BaseUnitTest extends TestCase {

  // ...

  @Before
  public void init() {
      mApplicationComponent = DaggerMockApplicationComponent.create();
      mNetworkComponent = DaggerMockNetworkComponent.builder()
        .mockApplicationComponent(mApplicationComponent)
        .build();
      MarinadeHelper.prepare(mApplicationComponent, mNetworkComponent);
  }
}

You can also register/unregister injectors dynamincally using Marinator. This helps if you need to register an injector for less than the entire lifecycle of the application.

Finally, in your classes, use Marinator to inject the necessary dependencies. The code doesn’t care whether the components were provided by the application, by a unit test, or by something else altogether:

public final class Recipe {
  @Inject Context mContext;

  public Recipe() {
    Marinator.inject(this);
  }
}

public final class Wine {
  @Inject Context mContext;

  public Wine() {
    Marinator.inject(this);
  }
}

Testing

To run the Marinator tests, use the command line. (Android Studio doesn’t play nicely with annotation processing in unit tests)

From the root directory of the project, run ./gradlew clean test to run the unit tests.

Source Courtesy: https://github.com/blueapron/marinator

RELATED ARTICLES

Most Popular