# 前言
[Android:dagger2讓你愛不釋手-基礎依賴注入框架篇](http://www.jianshu.com/p/cd2c1c9f68d4)這篇講解了Inject,Component,Module,Provides是如何構成dagger2整個**依賴注入框架**的

component_module_inject.png
因為dagger2的整個**依賴注入框架**已經構建完成,所以dagger2中剩下的Qualifier(限定符)、Singleton(單例)、Scope(作用域),SubComponent概念基本都是在對整個**依賴注入框架**進行細節上的完善。
我還是依然從抽象概念的角度講解,講解每個概念在整個**依賴注入框架**中到底起了什么作用,因為dagger2本身不容易上手,只有真正的了解了每個概念的作用,在使用時才會得心應手,大家別急,后面的章節會有dagger2的sample。
# 本節內容
* Qualifier(限定符)、Singleton(單例)、Scope(作用域)、Component的組織方式概念講解
* dagger2能帶來哪些實惠?
在講解時,我還依然沿用[上一節](http://www.jianshu.com/p/cd2c1c9f68d4)的講解方式,由簡入難不斷深入的進行。
# Qualifier(限定符)是什么鬼?
[上一節](http://www.jianshu.com/p/cd2c1c9f68d4)已經提到,Component是一個注入器(Injector),同時也起著橋梁的作用,一端是**創建類實例**端(創建類實例即負責生產類實例,下面會用該詞來指代),另一端是**目標類**端(目標類需要進行依賴初始化的類,下面都會用目標類一詞來指代),請看下圖:

新的關系.png
創建類實例有2個維度可以創建:
* 通過用Inject注解標注的構造函數來創建(以下簡稱Inject維度)
* 通過**工廠模式**的Module來創建(以下簡稱Module維度)
這2個維度是有優先級之分的,Component會首先從Module維度中查找類實例,若找到就用Module維度創建類實例,并停止查找Inject維度。否則才是從Inject維度查找類實例。所以創建類實例級別Module維度要高于Inject維度。
現在有個問題,**基于同一個維度條件下,若一個類的實例有多種方法可以創建出來,那注入器(Component)應該選擇哪種方法來創建該類的實例呢?**如下圖,基于Inject維度:

Qualifier_迷失.png
我把上面遇到的問題起個名字叫**依賴注入迷失**。
那么可以給不同的創建類實例的方法用**標識**進行標注,用**標識**就可以對不同的創建類實例的方法進行區分(標識就如給不同的創建類實例方法起了一個id值)。同時用**要使用的創建類實例方法的標識**對**目標類相應的實例屬性**進行標注。那這樣我們的問題就解決了,提到的**標識**就是Qualifier注解,當然這種注解得需要我們自定義。
Qualifier(限定符)就是解決**依賴注入迷失**問題的。
**注意**
dagger2在發現**依賴注入迷失**時在編譯代碼時會報錯。
# Scope(作用域)你真是挺坑的一個東東
我們暫且不介紹Singleton,因為它是Scope的一個默認實現,理解了Scope自然就理解Singleton了。
為什么要說Scope比較坑呢,在剛開始接觸Scope的時候,看了網上各種關于Scope的介紹,總結Scope的作用是:
> Dagger2可以通過自定義Scope注解,來限定**通過Module和Inject方式**創建的類的實例的生命周期能夠與目標類的生命周期相同。或者可以這樣理解:通過自定義Scope注解可以更好的管理創建的類實例的生命周期。
網上也有各種例子比如:自定義一個PerActivity注解,那創建的類實例就與Activity**“共生死“**。
或者用Singleton注解標注一個創建類實例的方法,該創建類實例的方法就可以創建一個唯一的類實例。
我對PerActivity和Singleton這些魔法性的注解產生了好奇,同時也產生了迷惑?迷惑是:
* 自定義Scope注解到底是怎么工作的
* 自定義的注解應該怎么定義名字,是不是定義一個名字就可以達到相應名字的效果。比如Singleton就可以實現單例,PerActivity就可以創建的類實例與Activity**“共生死“**,是不是我定義一個PerFragment的注解,同樣可以達到創建的類實例就與Fragment**“共生死“**。大家別對我這幼稚的想法千萬別見笑,當時我就把dagger2的Scope注解想的如此神通廣大了
于是乎我在網上進行各種搜索,并且分析源碼,最后的得到的結果也是讓我大吃一驚。自定義的Singleton、PerActivity注解根本就沒有這些功能。所以也可以說我被Scope坑了,或者是由于自己沒有對Scope有一個深入的理解,被自己坑了。這先賣個關子,后面會具體介紹Scope。
# Component組織方式重點中的重點
為什么說Component組織方式是重點中的重點呢?因為前面的各種概念都是在做鋪墊工作,現在我們會從一個app的角度來把這些概念融合在一起。
## 一個app中應該根據什么來劃分Component?
假如一個app(app指的是Android app)中只有一個Component,那這個Component是很難維護、并且變化率是很高,很龐大的,就是因為Component的職責太多了導致的。所以就有必要把這個龐大的Component進行劃分,劃分為粒度小的Component。那劃分的規則這樣的:
* 要有一個全局的Component(可以叫ApplicationComponent),負責管理整個app的全局類實例(全局類實例整個app都要用到的類的實例,這些類基本都是單例的,后面會用此詞代替)
* 每個頁面對應一個Component,比如一個Activity頁面定義一個Component,一個Fragment定義一個Component。當然這不是必須的,有些頁面之間的依賴的類是一樣的,可以公用一個Component。
第一個規則應該很好理解,具體說下第二個規則,為什么以頁面為粒度來劃分Component?
* 一個app是由很多個頁面組成的,從組成app的角度來看一個頁面就是一個完整的最小粒度了。
* 一個頁面的實現其實是要依賴各種類的,可以理解成一個頁面把各種依賴的類組織起來共同實現一個大的功能,每個頁面都組織著自己的需要依賴的類,一個頁面就是一堆類的組織者。
* **劃分粒度不能太小了**。假如使用mvp架構搭建app,劃分粒度是基于每個頁面的m、v、p各自定義Component的,那Component的粒度就太小了,定義這么多的Component,**管理、維護就很非常困難**。
所以以**頁面**劃分Component在管理、維護上面相對來說更合理。
## Singleton沒有創建單例的能力
為什么要談到創建單例呢?因為上面談到一個app要有一個全局的Component(我們暫且叫ApplicationComponent),ApplicationComponent負責管理整個app用到的全局類實例,那不可否認的是這些全局類實例應該都是單例的,那我們怎么才能創建單例?
> [上一節](http://www.jianshu.com/p/cd2c1c9f68d4)提到過Module的作用,Module和Provides是為解決第三方類庫而生的,Module是一個**簡單工廠模式**,Module可以包含創建類實例的方法
現在Modlule可以創建**所以類**的實例。同時
> Component會首先從Module維度中查找類實例,若找到就用Module維度創建類實例,并停止查找Inject維度。否則才是從Inject維度查找類實例。所以創建類實例級別Module維度要高于Inject維度。
所以利用以上2點,我們就可以創建單例。
* 在Module中定義創建全局類實例的方法
* ApplicationComponent管理Module
* 保證ApplicationComponent只有一個實例(在app的Application中實例化ApplicationComponent)
dagger2中正真創建單例的方法就是上面的步驟,全局類實例的生命周期也和Application一樣了,很關鍵的一點就是保證ApplicationComponent是只初始化一次。那估計有朋友就會問Singleton那豈不是多余的?
答案當然是 no no no。Singleton有以下作用:
* 更好的管理ApplicationComponent和Module之間的關系,保證ApplicationComponent和Module是匹配的。若ApplicationComponent和Module的Scope是不一樣的,則在編譯時報錯。
* 代碼可讀性,讓程序猿更好的了解Module中創建的類實例是單例。
## 組織Component
我們已經把一個app按照上面的規則劃分為不同的Component了,全局類實例也創建了單例模式。問題來了其他的Component想要把全局的類實例注入到目標類中該怎么辦呢?這就涉及到**類實例共享**的問題了,因為Component有管理創建類實例的能力。因此只要能很好的組織Component之間的關系,問題就好辦了。具體的組織方式分為以下3種:
**依賴方式**
一個Component是依賴于一個或多個Component,Component中的**dependencies**屬性就是依賴方式的具體實現
**包含方式**
一個Component是包含一個或多個Component的,被包含的Component還可以繼續包含其他的Component。這種方式特別像Activity與Fragment的關系。SubComponent就是包含方式的具體實現。
**繼承方式**
官網沒有提到該方式,具體沒有提到的原因我覺得應該是,該方式不是解決**類實例共享**的問題,而是從更好的管理、維護Component的角度,把一些Component共有的方法抽象到一個父類中,然后子Component繼承。
## Scope真正用武的時候了
前面也提到Scope的一些基本概念,那Scope的真正用處就在于Component的組織。
* 更好的管理Component之間的組織方式,不管是**依賴方式**還是**包含方式**,都有必要用自定義的Scope注解標注這些Component,這些注解最好不要一樣了,不一樣是為了能更好的體現出Component之間的組織方式。還有編譯器檢查有依賴關系或包含關系的Component,若發現有Component沒有用自定義Scope注解標注,則會報錯。
* 更好的管理Component與Module之間的匹配關系,編譯器會檢查 Component管理的Modules,若發現標注Component的自定義Scope注解與Modules中的標注創建類實例方法的注解不一樣,就會報錯。
* 可讀性提高,如用Singleton標注全局類,這樣讓程序猿立馬就能明白這類是全局單例類。

app的結構.png
# 總結
關于dagger2概念性的東西基本都已經介紹完畢,剩下的比如Lazy、Provide等注解就不做介紹了,它們太簡單了。同時也著重介紹了Scope,Qualifier等概念。還從整個app的角度來分析Component的組織方式。希望對大家能有幫助,因為dagger2上手還是比較復雜的,其實關鍵一點就是對于各種概念性的東東不了解,不知道它們到底有啥用途。所以我希望能幫到初學者對dagger2有一個整體性概念性的了解,然后在看網上的例子時能神清氣爽。
[Android:dagger2讓你愛不釋手-終結篇](http://www.jianshu.com/p/65737ac39c44)
**個人簡介**
本人是一名android開發工程師,開發android多年,若有志同道合的朋友想聯系我,可以加我的:qq/微信: 704451290
歡迎各位多多交流,轉載請標明出處。
作者:牛曉偉
鏈接:http://www.jianshu.com/p/1d42d2e6f4a5
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
- 0-發現
- AndroidInterview-Q-A
- Android能讓你少走彎路的干貨整理
- LearningNotes
- temp
- temp11
- 部分地址
- 0-待辦任務
- 待補充列表
- 0-未分類
- AndroidView事件分發與滑動沖突處理
- Spannable
- 事件分發機制詳解
- 1-Java
- 1-Java-01基礎
- 未歸檔
- 你應該知道的JDK知識
- 集合框架
- 1-Java-04合集
- Java之旅0
- Java之旅
- JAVA之旅01
- JAVA之旅02
- JAVA之旅03
- JAVA之旅04
- JAVA之旅05
- JAVA之旅06
- JAVA之旅07
- JAVA之旅08
- JAVA之旅09
- java之旅1
- JAVA之旅10
- JAVA之旅11
- JAVA之旅12
- JAVA之旅13
- JAVA之旅14
- JAVA之旅15
- JAVA之旅16
- JAVA之旅17
- JAVA之旅18
- JAVA之旅19
- java之旅2
- JAVA之旅20
- JAVA之旅21
- JAVA之旅22
- JAVA之旅23
- JAVA之旅24
- JAVA之旅25
- JAVA之旅26
- JAVA之旅27
- JAVA之旅28
- JAVA之旅29
- java之旅3
- JAVA之旅30
- JAVA之旅31
- JAVA之旅32
- JAVA之旅33
- JAVA之旅34
- JAVA之旅35
- 1-Java-05辨析
- HashMapArrayMap
- Java8新特性
- Java8接口默認方法
- 圖解HashMap(1)
- 圖解HashMap(2)
- 2-Android
- 2-Android-1-基礎
- View繪制流程
- 事件分發
- AndroidView的事件分發機制和滑動沖突解決
- 自定義View基礎
- 1-安卓自定義View基礎-坐標系
- 2-安卓自定義View基礎-角度弧度
- 3-安卓自定義View基礎-顏色
- 自定義View進階
- 1-安卓自定義View進階-分類和流程
- 10-安卓自定義View進階-Matrix詳解
- 11-安卓自定義View進階-MatrixCamera
- 12-安卓自定義View進階-事件分發機制原理
- 13-安卓自定義View進階-事件分發機制詳解
- 14-安卓自定義View進階-MotionEvent詳解
- 15-安卓自定義View進階-特殊形狀控件事件處理方案
- 16-安卓自定義View進階-多點觸控詳解
- 17-安卓自定義View進階-手勢檢測GestureDetector
- 2-安卓自定義View進階-繪制基本圖形
- 3-安卓自定義View進階-畫布操作
- 4-安卓自定義View進階-圖片文字
- 5-安卓自定義View進階-Path基本操作
- 6-安卓自定義View進階-貝塞爾曲線
- 7-安卓自定義View進階-Path完結篇偽
- 8-安卓自定義View進階-Path玩出花樣PathMeasure
- 9-安卓自定義View進階-Matrix原理
- 通用類介紹
- Application
- 2-Android-2-使用
- 2-Android-02控件
- ViewGroup
- ConstraintLayout
- CoordinatorLayout
- 2-Android-03三方使用
- Dagger2
- Dagger2圖文完全教程
- Dagger2最清晰的使用教程
- Dagger2讓你愛不釋手-終結篇
- Dagger2讓你愛不釋手-重點概念講解、融合篇
- dagger2讓你愛不釋手:基礎依賴注入框架篇
- 閱讀筆記
- Glide
- Google推薦的圖片加載庫Glide:最新版使用指南(含新特性)
- rxjava
- 這可能是最好的RxJava2.x入門教程完結版
- 這可能是最好的RxJava2.x入門教程(一)
- 這可能是最好的RxJava2.x入門教程(三)
- 這可能是最好的RxJava2.x入門教程(二)
- 這可能是最好的RxJava2.x入門教程(五)
- 這可能是最好的RxJava2.x入門教程(四)
- 2-Android-3-優化
- 優化概況
- 各種優化
- Android端秒開優化
- apk大小優化
- 內存分析
- 混淆
- 2-Android-4-工具
- adb命令
- 一鍵分析Android的BugReport
- 版本控制
- git
- git章節簡述
- 2-Android-5-源碼
- HandlerThread 源碼分析
- IntentService的使用和源碼分析
- 2-Android-9-辨析
- LRU算法
- 什么是Bitmap
- 常見圖片壓縮方式
- 3-Kotlin
- Kotlin使用筆記1-草稿
- Kotlin使用筆記2
- kotlin特性草稿
- Kotlin草稿-Delegation
- Kotlin草稿-Field
- Kotlin草稿-object
- 4-JavaScript
- 5-Python
- 6-Other
- Git
- Gradle
- Android中ProGuard配置和總結
- gradle使用筆記
- Nexus私服搭建
- 編譯提速最佳實踐
- 7-設計模式與架構
- 組件化
- 組件化探索(OKR)
- 1-參考列表
- 2-1-組件化概述
- 2-2-gradle配置
- 2-3-代碼編寫
- 2-4-常見問題
- 2-9-值得一讀
- 8-數據結構與算法
- 0臨時文件
- 漢諾塔
- 8-數據-1數據結構
- HashMap
- HashMap、Hashtable、HashSet 和 ConcurrentHashMap 的比較
- 遲到一年HashMap解讀
- 8-數據-2算法
- 1個就夠了
- Java常用排序算法(必須掌握的8大排序算法)
- 常用排序算法總結(性能+代碼)
- 必須知道的八大種排序算法(java實現)
- 9-職業
- 閱讀
- 書單
- 面試
- 面試-01-java
- Java面試題全集駱昊(上)
- Java面試題全集駱昊(下)
- Java面試題全集駱昊(中)
- 面試-02-android
- 40道Android面試題
- 面試-03-開源源碼
- Android圖片加載框架最全解析(二),從源碼的角度理解Glide的執行流程
- 面試-07-設計模式
- 面試-08-算法
- 面試-09-其他
- SUMMARY
- 版權說明
- temp111