iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 16
0
自我挑戰組

Android Architecture 及 Unit Test系列 第 16

[Day 16] Dagger 2:Part 4 Refactor

  • 分享至 

  • xImage
  •  

其實在昨天已經成功把 Dagger 加入到專案內了,但我今天想要再整理一下 Dagger 的 Module 及讓 Dagger 的組件也能有架構的層次感。

另外因為我們還沒開始寫邏輯層的程式碼,所以今天會整理 Dagger 提供依賴給 data layer 及 presentation layer 的模塊,下面就開始進行吧。

Data Layer

這邊我重新整理了資料層的依賴,統一透過 DataModule 獲得實體,而 DataModule 內則還有 4 個子 Module :

  • DatabaseModule:定義 Database 需要的依賴
  • NetworkModule:定義網路會需要的依賴,如 Retrofit 等,目前沒有實作
  • DataSourceModule:定義 DataSource 依賴
  • RepositoryBinds:定義 repository 的依賴

其中如果有要用到 @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 看看

Presentation Layer

在這裏我運用了 @Scope 來幫助我管理 Activity 及 Fragment ,那麼先來介紹一下 Scope 吧。

Scope

Scope 直譯過來就是作用域的意思,指的是定義一個依賴作用的範圍。

如果為依賴加上 @Provides 或是 @Inject 後 Component 每次調用時會創建一個新的實例,此時加上 @Scope 後就可以表明在這個作用域 (Component) 之下,使用的實例會是同一個。

因此如果能夠確保 Component 的生命週期,也意味著我們能夠確保 Component 裡註解了 @Scope 的實例的生命週期。

從這個角度來看,就能很好理解 @Singleton 這個註解了, @Singleton 其實也是透過 @Scope 實現的,事實上它代表的是在一個 Component 內的唯一實例,因此假如有多個 Component 的實例的話, @Singleton 就無法保證單例了。

Scope 較常見的情境是用來管理像是 Activity 或是 Fragment 之類的元件,那麼就來看看怎麼實現吧。

首先先建立 ActivityScopeFragmentScope

@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 無論如何在性能方面是要強過其他框架許多的。


上一篇
[Day 15] Dagger 2:Part 3 Complete Dagger
下一篇
[Day 17] Domain layer:UseCase
系列文
Android Architecture 及 Unit Test30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言