Dagger2,依賴注入框架,一個剛接觸時感受麻煩,用久了就會「嘴上說不要,身體卻很誠實」的開發潤滑劑(◐‿◑)。(本文爲拖更而生)java
誰用誰知道Σ( ̄。 ̄ノ)ノ,如絲般順滑,奶不死的Dagger2 ,主要優點體如今:android
new A(new B(new C()))
。@Scope
規範實例的生命週期。@Component
、@Module
、@Inject
的規範實現。Dagger2 能夠理解爲一套開發規範,遵照這套規範編寫的代碼,經過Dagger2 的運行時註解,在編譯時自動生成模版代碼,已達到注入和複用的目的。git
自動生成的代碼,通常存在路徑build\generated\source\apt
下。那麼瞭解完這套模版規範,Dagger2 將再也不神祕,「深刻淺出」「指日可待」(˶‾᷄ ⁻̫ ‾᷅˵)啊。github
關於運行時註解不瞭解的可查閱:《Android註解快速入門和實用解析》bash
讓咱們循環漸進的開始吧。框架
首先看下圖,Dagger2中主要的三個註解是 :@Inject
、@Component
、@Module
。ide
它們是最基礎,也是使用最多註解,咱們將從它們身上開始「摸索」Σ( ̄。 ̄ノ)ノ。函數
@Inject
指向須要構成注入的類和環境。@Module
提供生成對象所需的參數。(通常是在 @Inject 註解的對象,其構造函數沒法添加 @Inject時使用。)@Component
做爲中間橋樑鏈接注入對象和工廠。 一切的「插入」從它開始!@Inject
指定須要注入的類和環境,以下方圖2,TasksActivity 中 mTasksPresenter 是被Inject註解的對象,同時TasksPresenter 的構造函數也被Inject註解。ui
內部成員被 @Inject
註解時,如 mTasksPresenter,Dagger2 就會找 TasksPresenter 中被 @Inject
註解的構造函數,若是找到了,便對其構建注入。this
那麼問題來了!這時候若是 TasksPresenter 的構造方法爲空構造方法,便萬事大吉。惋惜現實老是那麼騷!圖中 TasksPresenter 的構造函數有參,須要 tasksRepository 和 tasksView 兩個參數,那即是後面的@Module
的用武之地,它將提供「後門注入」支持。
既然知道 @Module
的做用,咱們先繼續往下走,構造方法被註解的類,會生成一個繼承Factory
的類,如 TasksPresenter 生成 TasksPresenter_Factory , 以下圖3,這是由Dagger2的自動編譯生成的,這個工廠用於提供實例化類,其中的get()
方法即是在注入時被調用。
固然,你徹底能夠不關心這些。可是瞭解這些,有時候能夠更愉悅的裝逼(-_^),因此我選擇憋着往下看。
繼續深刻,以下圖四,在被 @Inject
註解的內部參數或方法,會生成對應的繼承MembersInjector的類, 如 TasksActivity_MembersInjector。在它調用.inject(this);
時,實際上就是調用了上面 TasksPresenter_Factory 的 get()
方法注入進去的。
看到沒,這不就聯繫起來了麼,城市套路深啊,並且還很滑!(˶‾᷄ ⁻̫ ‾᷅˵)
上面說過,由於構造方法包含參數,而所包含的參數,其構造方法沒法被 @Inject
註解,這時候就須要 @Module
提供「後門」。
舉個栗子:類A的構造方法須要類B,但類B的構造方法沒法添加 @Inject
註解。這時候經過 @Module
,在內部使用 @Provides
註解的以provide開頭的方法,這些方法就是所提供注入所需的依賴,如圖5。
一樣是自動生成模版代碼,@Module
註解的類中,每個 @Provides
修飾的方法,都會生成一個工廠類,如圖六下生成:TasksModule_ProvideTasksViewFactory ,經過get()
方法提供注入時所需的參數。
這時候回到圖四,能夠看到這些get()
方法,即是在 TasksPresenter_Factory 的 get()
被調用時使用 看到沒,這不又聯繫起來了麼,套路套路啊!(˶‾᷄ ⁻̫ ‾᷅˵)。
作了那麼多,總得用起來吧。Component 就是將 @Inject
和 @Module
聯繫起來的橋樑,從 @Module
中獲取依賴,並將依賴注入給 @Inject
註解的參數。
如圖七,@Component
接口指定了使用的 Module 和依賴的 Component。是的,Component依賴,這樣更有利於代碼的複用。
@Component
的接口的內部方法簡單可分爲:
@Module
有 @Provide提供的。 一樣的套路,TaskComponent會生成 DaggerTaskComponent 類,這個類即是咱們須要使用的對象,如圖八圖九,能夠看到上面生成的對象,都是在其中被使用,最後經過咱們定義好的 inject()
方法,以下方代碼實現注入效果。
//注入咯注入咯
DaggerTasksComponent.builder()
.tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
.tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
.inject(this);複製代碼
怎麼樣,這樣的套路清晰了麼?(˶‾᷄ ⁻̫ ‾᷅˵),簡單總結起來其實以下方這樣,須要咱們動手的,其實並很少:
@Inject
註解須要注入的參數和方法。@Module
提供構造方法中沒法註解的參數。@Component
連起起來,而且調用注入。來簡單總結下生成模版的關係,不感興趣的能夠略過···吧(#゚Д゚):
註解構造方法,生成的 TasksPresenter_Factory
,內部經過 create
方法 建立Factory,經過get
方法 提供 TasksPresenter
對象。
TasksModule_ProvideTasksViewFactory
,由Module中@Provide註解的方法生成,內部經過 create
方法建立,經過 get
方法提供 TasksPresenter_Factory
在建立 TasksPresenter
須要的參數。
TasksComponent生成DaggerTasksComponent
初始化時 ,經過 TasksModule_ProvideTasksViewFactory
做爲參數建立了 TasksPresenter_Factory
, 在TasksActivity_MembersInjector
中,經過injectMembers
方法,利用 TasksPresenter_Factory
的 get
實現注入。
DaggerTasksComponent
中外部提供Builder設置方法,和依賴的Component與module數量有關。若是依賴的未被使用,會有@deprecated提示。
讓咱們再稍微深刻一點去了解Dagger2吧,生命週期是值得關心的。好比內置的 @Singleton
註解,字面上就是單例的意思,可是實際上直接使用 @Singleton
並不會有單例的效果。
@Singleton
其實是一種規範註解,它屬於 @Scope
註解的一種,正如字面上所示,它表明着單例的生命週期,事實上 @Scope
是一種做用域註解,經過Component、Module一其配種使用,才能達到做用域的效果。
如上圖十,下圖11,12中, TasksRepository、TasksRepositoryComponent 、TasksRepositoryModule 都被 @Singleton
註解了,可是這並不表明着他們就是單例,只是經過註解限定做用域,而且在字面上代表了它們單例。
真正的單例效果, 是下圖13中在 ToDoApplication
中 DaggerTasksRepositoryComponent
的建立。粗俗點說,就是由於Application中的 mRepositoryComponent
對象只有一個,因爲做用域 @Scope
的做用,那麼getTasksRepository()
獲取的TasksRepository
達到了單例的效果。
這裏理解下來,以下方代碼,咱們建立一個@ActivityScoped
的Scope註解,限定注入對象 TasksPresenter
的生命週期是和Activity相關的,而後咱們在 TasksActivity
中,調用 DaggerTasksComponent
注入。由於 DaggerTasksComponent
在 TasksActivity
中惟一,因此 TasksPresenter
在Activity中也惟一。
@Documented
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface ActivityScoped {
}
········
@ ActivityScoped
public class TasksRepository implements TasksDataSource{
···
}
·······
public class TasksActivity extends AppCompatActivity {
@Inject TasksPresenter mTasksPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.tasks_act);
// Create the presenter
DaggerTasksComponent.builder()
.tasksRepositoryComponent(((ToDoApplication) getApplication()).getTasksRepositoryComponent())
.tasksPresenterModule(new TasksPresenterModule(tasksFragment)).build()
.inject(this);
}複製代碼
上面這段看起來是否是不少餘?有點廢話?
可是確也是最好理解的,若是換個方法,好比把 @Inject TasksPresenter mTasksPresenter;
寫在Fragment,DaggerTasksComponent
在Activity構建,而後在多個Fragment的onCreateView調用inject(this)
呢?
很明顯注入的mTasksPresenter對象就是Activity生命週期,而和Fragment生命週期無關,而且因爲Dagger2內部的限定,Scope能夠更好的規範Module和Component的生命週期。
還有更多的場景和規範要求,這裏就不一一展開了,有興趣深刻了解的可谷歌之。(確定不是由於懶得展開!!)
簡單的理解,它就是一個Tag標記的做用。
以下方代碼,咱們定義了Remote和Local兩種@Qualifier
註解,在Module中,提供的 TasksDataSource
經過Remote和Local兩種Tag區分,這樣在注入的時候,也能夠經過Tag指定注入參數。
很好理解吧!這裏就不展開了,一展開了又是一堆····╮(╯▽╰)╭,是時候收尾了,你(wo)也須要休息休息了。
//類型一
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Remote {
}
······
//類型二
@Qualifier
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Local {
}
······
//提供時區分類型
@Module
abstract class TasksRepositoryModule {
@Singleton
@Local
abstract TasksDataSource provideTasksLocalDataSource(TasksLocalDataSource dataSource);
@Singleton
@Remote
abstract TasksDataSource provideTasksRemoteDataSource(FakeTasksRemoteDataSource dataSource);
}
······
//根據類型注入
@Inject
TasksRepository(@Remote TasksDataSource tasksRemoteDataSource,
@Local TasksDataSource tasksLocalDataSource) {
mTasksRemoteDataSource = tasksRemoteDataSource;
mTasksLocalDataSource = tasksLocalDataSource;
}複製代碼
android-architecture : todo-mvp-dagger2 分支中有詳細的demo演示。
LazyRecyclerAdapter :我的在這個開源項目包含有Dagger2在java和kotlin中的使用demo。