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.