其實在昨天已經成功把 Dagger 加入到專案內了,但我今天想要再整理一下 Dagger 的 Module 及讓 Dagger 的組件也能有架構的層次感。
另外因為我們還沒開始寫邏輯層的程式碼,所以今天會整理 Dagger 提供依賴給 data layer 及 presentation layer 的模塊,下面就開始進行吧。
這邊我重新整理了資料層的依賴,統一透過 DataModule
獲得實體,而 DataModule
內則還有 4 個子 Module :
其中如果有要用到 @Binds
方法時會再獨立出一個叫 XXXBinds
的 Module 以示區別,完成後大致如下:
@Module(
includes = [
DataSourceModule::class,
RepositoryBinds::class,
DatabaseModule::class,
NetworkModule::class
]
)
class DataModule {
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class TasksLocalData
@Qualifier
@Retention(AnnotationRetention.RUNTIME)
annotation class TasksRemoteData
}
因為程式碼太多就不貼上來了,有興趣可以到 DataModule 的 Gist 看看
在這裏我運用了 @Scope
來幫助我管理 Activity 及 Fragment ,那麼先來介紹一下 Scope 吧。
Scope 直譯過來就是作用域的意思,指的是定義一個依賴作用的範圍。
如果為依賴加上 @Provides
或是 @Inject
後 Component 每次調用時會創建一個新的實例,此時加上 @Scope
後就可以表明在這個作用域 (Component) 之下,使用的實例會是同一個。
因此如果能夠確保 Component 的生命週期,也意味著我們能夠確保 Component 裡註解了 @Scope
的實例的生命週期。
從這個角度來看,就能很好理解 @Singleton
這個註解了, @Singleton
其實也是透過 @Scope
實現的,事實上它代表的是在一個 Component 內的唯一實例,因此假如有多個 Component 的實例的話, @Singleton
就無法保證單例了。
Scope
較常見的情境是用來管理像是 Activity 或是 Fragment 之類的元件,那麼就來看看怎麼實現吧。
首先先建立 ActivityScope
及 FragmentScope
:
@Scope
@Retention(AnnotationRetention.RUNTIME)
@MustBeDocumented
annotation class ActivityScoped
@Scope
@Retention(AnnotationRetention.RUNTIME)
@Target(AnnotationTarget.TYPE, AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class FragmentScoped
將提供 TasksFragment 及 TasksViewModel 的依賴方法整理到 TasksModule
裡,並在提供 TasksFragment 依賴的方法上加上 @FragmentScoped
:
@Module
internal abstract class TasksModule {
@FragmentScoped
@ContributesAndroidInjector
internal abstract fun contributeTasksFragment(): TasksFragment
@Binds
@IntoMap
@ViewModelKey(TasksViewModel::class)
abstract fun bindTasksViewModel(viewModel: TasksViewModel): ViewModel
}
接著再來整理 TasksActivity ,建立 ActivityBindingModule
負責所有 Activity 的依賴工作:
@Module
abstract class ActivityBindingModule {
// Android Activity Injector 依此類推
@ActivityScoped
@ContributesAndroidInjector(
modules = [
TasksActivityBinds::class,
TasksModule::class
]
)
abstract fun contributeTasksActivity(): TasksActivity
}
@Module
abstract class TasksActivityBinds {
// 如果有需要,在這裏 bind Activity 的 View Model 或是必要的依賴物
}
同樣在提供 TasksActivity 依賴的方法上加上 @ActivityScoped
,以及相依於此 Activity 的 Fragment Module ,基本上就大功告成了。
說實話 Dagger 2 的學習曲線真的不是很友善,我在學習了 Dagger 後更加深了改用 koin 的想法 XD 。
畢竟單就使用難易度上其他框架真的容易許多,但是這幾天為了研究 Dagger 讓我對於實例的各種依賴關係有了許多不同的看法,個人認為這才是應該要學習的。
當然也希望其他學習 Dagger 2 的同學不要氣餒 (? ,剛開始可以先從建立幾個基本的 Module 開始著手,一旦成功建起 DI 框架後對於專案後續開發、測試、維護等都非常有幫助,而且 Dagger 無論如何在性能方面是要強過其他框架許多的。