Injecting into Android Framework Classes with Dagger2: (Day 14)

Photo by Andrew Neel on Unsplash

Injecting into Android Framework Classes with Dagger2: (Day 14)

Injecting dependencies into Android Framework Classes is a common challenge when using Dagger2, primarily because these classes are instantiated by the Android system and not by the developer directly. This means that the usual method of constructor injection isn't applicable. Let's delve into how Dagger2 handles this:

Injecting into Android Framework Classes with Dagger2:

1. The Challenge:

Android Framework Classes like Activity, Fragment, Service, BroadcastReceiver, and ContentProvider are instantiated by the Android system. This means you can't directly use constructor injection, as you don't control the creation of these objects.

2. The Solution: Field Injection

Since constructor injection isn't an option, we rely on field injection. This means that after the Android system creates the object (e.g., an Activity), Dagger2 will inject the required dependencies directly into the annotated fields of that object.

Since we have already learnt about field injection in our previous blog posts, we won't go into much detail here, instead we'll learn a new way of injecting into Android Framework Types.

3. Dagger Android Extensions:

To simplify the process of injecting into Android Framework Classes, Dagger2 provides the Dagger Android extensions. These extensions offer tools and conventions to streamline the injection process.

a. AndroidInjector: Dagger Android introduces AndroidInjector, which is a helper interface to facilitate injection into Android Framework Classes.

b. @ContributesAndroidInjector: This annotation simplifies the creation of subcomponents. Instead of writing a whole subcomponent for each Android Framework Class, you can use @ContributesAndroidInjector to generate them automatically.

c. DaggerApplication, DaggerActivity, DaggerFragment, etc.: These are extended versions of Android Framework Classes that have built-in support for Dagger2 injection. By subclassing these, you can reduce boilerplate code related to injection.

Let's try to implement it in our own example and see how it reduces the boilerplate code.

Step 1

First of all, we need to add proper dependencies to our gradle for supporting Dagger Android as shown below.

//Dagger Implementation
    def dagger_version = '2.46.1'
    implementation "com.google.dagger:dagger:$dagger_version"
    kapt "com.google.dagger:dagger-compiler:$dagger_version"
    //dagger android (new dependencies added)
    implementation "com.google.dagger:dagger-android:$dagger_version"
    implementation "com.google.dagger:dagger-android-support:$dagger_version"
    kapt "com.google.dagger:dagger-android-processor:$dagger_version"

Step 2

Now, we need to update our ComputeComponent class and implement AndroidInjector to it as shown.

@Singleton
@Component (modules = {AppModule.class, AndroidSupportInjectionModule.class, NetworkModuleSecond.class})
public interface ComputeComponent extends AndroidInjector<MyApp> {

    @Component.Factory
    interface Factory {
        ComputeComponent create(@BindsInstance @Named("delay") int delay
                , @BindsInstance @Named("status")int status,
                                NetworkModuleSecond networkModuleSecond);
    }
}

Notice that we have removed the reference to the subcomponent ActivityComponent because now this subcomponent will be autogenerated by Dagger2

Step 3

Since the @ContributesAndroidInjector annotation is designed to be used within a @Module to generate subcomponents and their builders for you. We need to first create an AppModule as shown below.

@Module
public abstract class AppModule {

    @PerActivity
    @ContributesAndroidInjector
    public abstract MainActivity contributeMainActivity();
}

Step 4

Now we need to extend the respective Android dagger classes to our Framework Types as shown below.

public class MyApp extends DaggerApplication {
    private ComputeComponent component;
    @Override
    public void onCreate() {
        super.onCreate();

        component = DaggerComputeComponent.factory().create(3000, 10, new NetworkModuleSecond());
    }

    public ComputeComponent getAppComponent(){
        return component;
    }

    @Override
    protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
        return DaggerComputeComponent.factory().create(1000, 10, new NetworkModuleSecond());
    }
}
class MainActivity : DaggerAppCompatActivity() {
//... the rest of the code as usual
}

And that's it!

If we run our app again, you'll see that the app behaves precisely as it used to behave earlier, but now we do not need our ActivityComponent class, and also we do not need to worry about properly instantiating our subcomponents in activities and managing its scope. All of that is now directly handled by Dagger2.

This might seem like a lot of work just to reduce a bit of boiler plate code, but in the long run as the app size and the amount of code increases, these helper classes reduce the overhead of creating a lot of classes significantly, which ultimately makes both testings as well as code maintenance easier.

5. Benefits of Using Dagger Android Extensions:

  • Reduced Boilerplate: No need to manually create subcomponents for each Android Framework Class.

  • Conventions: By following Dagger Android conventions, your code becomes more standardized and easier to understand.

Conclusion:

Injecting into Android Framework Classes with Dagger2 requires a different approach than typical constructor injection due to the Android system's control over object instantiation. By using field injection and leveraging Dagger Android extensions, developers can efficiently manage dependencies in Android applications while maintaining clean and organized code.