Dependency Injection in Android With Dagger2 (7)
Static Provider Methods and Component Builder
One fact: it is more performant to make the all provider methods inside Modules static.
But for Modules that have bootstrap dependencies, this is impossible. Let’s take ActivityModule
as example.
@Module
class ActivityModule(private val activity: AppCompatActivity) {
@Provides
fun activity(): AppCompatActivity = activity
@Provides
fun layoutInflater(activity: AppCompatActivity): LayoutInflater = LayoutInflater.from(activity)
@Provides
fun fragmentManager(activity: AppCompatActivity) = activity.supportFragmentManager
@Provides
@ActivityScope
fun screensNavigator(activity: AppCompatActivity) = ScreensNavigator(activity)
}
We can easily make fun layoutInflater()
, fragmentManager()
and screensNavigator
static methods. But we cannot do it for fun activity()
, because static method does not have access to private property activity
.
This means we cannot pass anything into Module’s constructor. But then where can we provide activity
? The answer is ActivityComponent
can provide it directly.
And in order to do this, we need some extra stuff.
First, let’s recap how ActivityComponent
is created.
@AppScope
@Component(modules = [AppModule::class])
interface AppComponent {
fun newActivityComponent(activityModule: ActivityModule): ActivityComponent
fun newServiceComponent(serviceModule: ServiceModule): ServiceComponent
}
open class BaseActivity : AppCompatActivity() {
...
val activityComponent: ActivityComponent by lazy {
appComponent.newActivityComponent(ActivityModule(this))
}
...
}
So basically this is how ActivityComponent
created: appComponent.newActivityComponent(ActivityModule(this))
.
So if now ActivityModule
does not take in activity
, then the only place to provide activity
is ActivityComponent
itself.
But as you can see, we don’t have control over the creation of ActivityComponent
. So the solution to this problem is Component Builder
.
@ActivityScope
@Subcomponent(modules = [ActivityModule::class])
interface ActivityComponent {
fun newPresentationComponent(): PresentationComponent
@Subcomponent.Builder
interface Builder {
@BindsInstance
fun activity(activity: AppCompatActivity): Builder
fun activityModule(module: ActivityModule): Builder
fun build(): ActivityComponent
}
}
So we create ActivityComponent.Builder
so that we can describe how we want to build an ActivityComponent
: need ActivityModule
and an instance of activity
.
And once we declare this Builder
interface, we cannot expose function that returns ActivityComponent
directly, we need to change the return type to the Builder
.
@AppScope
@Component(modules = [AppModule::class])
interface AppComponent {
fun newActivityComponentBuilder(): ActivityComponent.Builder
fun newServiceComponent(serviceModule: ServiceModule): ServiceComponent
}
Now we are able to create ActivityComponent
inside BaseActivity
like this:
open class BaseActivity : AppCompatActivity() {
...
val activityComponent: ActivityComponent by lazy {
appComponent.newActivityComponentBuilder()
.activity(this)
.activityModule(ActivityModule)
.build()
}
...
}
And finally the ActivityModule
looks like:
@Module
object ActivityModule {
@Provides
@ActivityScope
fun screensNavigator(activity: AppCompatActivity) = ScreensNavigator(activity)
@Provides
fun layoutInflater(activity: AppCompatActivity): LayoutInflater = LayoutInflater.from(activity)
@Provides
fun fragmentManager(activity: AppCompatActivity) = activity.supportFragmentManager
}
Share this post
Twitter
Google+
Facebook
Reddit
LinkedIn
StumbleUpon
Email