Skip to content

Android library which simplifies the creation of a Dagger2 dependency graph while helping mentain the code more readable

License

Notifications You must be signed in to change notification settings

AlexDochioiu/DaggerSharpener

Repository files navigation

DaggerSharpener

bintray License Build Status codecov

DaggerSharpener is an Android library which simplifies the creation of a Dagger2 dependency graph while helping mentain the code more readable. This library comes as an extension for Dagger2.

In this repository you can find an example project showing its usage as well as the library projects. The example is replicating the following dagger2 tutorial project (using Dagger2 + DaggerSharpener): https://github.com/patrick-doyle/dagger2-tutorial

Installation with Android Gradle

// Add DaggerSharpener and Dagger2 dependencies
dependencies {
    implementation 'com.github.alexdochioiu:daggersharpener:0.1.0'
    annotationProcessor 'com.github.alexdochioiu:daggersharpener-processor:0.1.0'
    
    implementation 'com.google.dagger:dagger:2.17'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.17'
}

Usage Example

Step 1: Create a normal dagger2 module

@Module
public class MyModule {
    @Provides
    Object myObject() {
        return new Object();
    }
}

Note: As you can see, no scope has yet been given to the object. This will be addressed in the following steps.

Step 2: Creating the Application (Sharp) Component ()

@SharpComponent(
        modules = MyModule.class // array of multiple modules can be provided as {A.class, B.class, ...}
)
public class MyApplication extends Application {
}

Step 2.1: Build the project

After building, two files are generated:

SharpMyApplicationComponent.java -- (Names is formed as: Sharp + Class Name + Component)

@Component
@SharpMyApplicationScope
public interface SharpMyApplicationComponent {
  MyApplication inject(MyApplication thisClass);
}

SharpMyApplicationScope.java -- (Names is formed as: Sharp + Class Name + Scope)

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface SharpMyApplicationScope {}

Step 2.2 (Optional): Go back to the module and add the scope for the provided object(s)

@Module
public class MyModule {
    @Provides
    @SharpMyApplicationScope
    Object myObject() {
        return new Object();
    }
}

Step 2.3 : Inject the dependencies in MyApplication

@SharpComponent(
        modules = MyModule.class
)
public class MyApplication extends Application {

    private SharpMyApplicationComponent sharpComponent;
    
    @Inject
    Object injectedObject;

    @Override
    public void onCreate() {
        super.onCreate();
        
        // Name is: Dagger + Sharp + Class Name + Component
        sharpComponent = DaggerSharpMyApplicationComponent.builder().build();
        sharpComponent.inject(this);

        Log.d("MyApplication", "onCreate: " + injectedObject.toString());
    }

    public SharpMyApplicationComponent getSharpComponent() {
        return sharpComponent;
    }
}

Note: By running the code and checking logcat, you will see that the injectedObject is non-null. This indicates our object got successfully injected.

Step 3: Creating the Activity (Sharp) Component

@SharpComponent(
        dependencies = SharpMyApplicationComponent.class
)
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Note: The new Sharp Activity Component depends on SharpMyApplicationComponent

Step 3.1: Build the project

After building, two new files are generated:

SharpMainActivityComponent.java

@Component
@SharpMainActivityScope
public interface SharpMainActivityComponent {
  MainActivity inject(MainActivity thisClass);
}

SharpMainActivityScope.java

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface SharpMainActivityScope {}

Step 3.2: Expose dependencies from MyApplication to its dependants

@SharpComponent(
        modules = MyModule.class,
        provides = Object.class // Array of dependencies can be provided as {A.class, B.class, etc.}
)
public class MyApplication extends Application {
  ...
}

Step 3.3 : Inject the dependencies in MainActivity

@SharpComponent(
        sharpDependencies = MyApplication.class // The class(es) annotated with @SharpComponent
)
public class MainActivity extends AppCompatActivity {

    @Inject
    Object myObject;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerSharpMainActivityComponent.builder()
                .sharpMyApplicationComponent(((MyApplication) getApplication()).getSharpComponent())
                .build()
                .inject(this);

        Log.d("MainActivity", "onCreate: " + myObject.toString());
    }
}

Note: By running the code and checking logcat, you will see that injectedObject from MyApplication and myObject from MainActivity are indeed the same instance (as long as you did the step 2.2). If you did not do step 2.2, the instance will be different for the two objects.

Step 4: Enjoy

Using DaggerSharpener will still allow you to use @Inject on constructors. Also, the generated scopes can be safely used on the classes which inject constructors.

For some docs, keep on reading below!

SharpComponent explained

modules

Takes an array of dagger2 modules

sharpDependencies

Takes an array of classes annotated as @SharpComponent which will serve as dependencies for this new sharp component which is to be generated.

dependencies

Takes an array of dagger2 components which will serve as dependencies for this new sharp component which is to be generated. This is to allow the developer to mix manually created dagger2 components and sharp components. Note: sharpDependencies and dependencies can be used together safely

scope

If you want to use a manually created scope, you can give it there. If you do so, no scope will be generated for this sharp component.

provides

The array of (un-named) classes to be provided to the dependants of this sharp component

providesNamed (to be released in version 0.2.0)

The array of @NamedPair entries. Each entry has a String name and a class (see below)

SharpComponent example (slightly more complex than before)

@SharpComponent(
        modules = {GithubServiceModule.class, PicassoModule.class},
        sharpDependencies = MyApplication.class,
        //dependencies = SomeDaggerComponent.class,
        scope = MyCustomScope.class,
        provides = {GithubService.class, Resources.class},
        providesNamed = {
                @NamedPair(aName = "myPicasso", aClass = Picasso.class),
                @NamedPair(aName = "appContext", aClass = Context.class),
                @NamedPair(aName = "activityContext", aClass = Context.class)
        }
)
public class MyFragment extends Fragment {
  ...
}

The code above would generate the following component

@Component(
    modules = {GithubServiceModule.class, PicassoModule.class},
    dependencies = {SharpMyApplicationComponent.class}
)
@MyCustomScope
public interface SharpMyFragmentComponent {
  MyFragment inject(MyFragment thisClass);

  GithubService provideGithubService();

  Resources provideResources();

  @Named("myPicasso")
  Picasso providePicasso_myPicasso();

  @Named("appContext")
  Context provideContext_appContext();

  @Named("activityContext")
  Context provideContext_activityContext();
}