讀懂 Dagger2 源碼

理解 Dagger2 的源碼
Dagger2 幫助我們將被注入類自動地注入到目標類堡僻,有利解耦找岖,從而讓維護項目的依賴關系變得輕松姐刁。

通過代碼實現(xiàn)一下 dagger 是如何實現(xiàn)依賴注入铃诬,我們創(chuàng)建一個類 DataManager 用作被注入的類。

public class DataManager {

    private static final String TAG = "DataManager";

    public DataManager() {
        Log.d(TAG, "DataManager: ininial");
    }
}

然后我們需要 DataManager 注入到目標中 MainActivity座哩,因為 MainActivity 并不是由我們開發(fā)人員直接創(chuàng)建的徒扶,是由系統(tǒng)創(chuàng)建,所以我們需要將一些依賴以屬性的形式進行注入

public class MainActivity extends AppCompatActivity {

    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
    }
}

好我們就開始來實現(xiàn)根穷,要進行注入姜骡,如果熟悉 dagger 知道我們需要一個 module 類和一個 component 接口

首先 Module 負責提供要注入的類的對象,在 Dagger 中我們需要 @Module 來修飾為了是 apt 能夠識別到這個類進行代碼生成工作屿良。

public class AppModule {

    public DataManager provideManager(){
        return new DataManager();
    }
}

然后 Component 負責將被注入類注入到目標類中圈澈,在 Dagger 中我們需要注解 @Component 以及將 @Component 以及已經(jīng)定義依賴的 module

public interface AppComponent {
    void inject(MainActivity activity);
}

最后我們還需要 MainActivity 中做一些事

public class MainActivity extends AppCompatActivity {

    public DataManager dataManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ScAppComponent.builder()
                .appMoudle(new AppModule())
                .build() //使用構(gòu)建者模式創(chuàng)建一個 Component 實例
                .inject(this);
    }
}

需要做兩件事

  1. 將 DataManager 作為屬性,需要是 public 類型

在編譯過程會用到 apt 技術生成一些類用來實現(xiàn)尘惧,這里我們就手動寫這些類

通常使用時我們通常會以這種形式來實現(xiàn)康栈,

DaggerAppComponent
    .builder()
    .appModule(new AppModule())
    .build()
    .inject(this)

appModule 應該代碼生成時根據(jù) AppModule 生成方法,通過 builder 可看出來這里使用構(gòu)建者的設計模式喷橙。我們這里手動地完成代碼生成以便了解 dagger 是如何將類 DataManager 注入到 MainActivity

我們先寫 ScAppComponent 這個自動生成的類谅将,在 Dagger 命名應該為 DaggerAppComponent,這里我用我習慣的 Sc 開頭以便區(qū)別重慢,這里 ScAppComponent 實現(xiàn)之前定義好的 AppComponent 接口,實現(xiàn) inject 方法逊躁,這個方法指定要將 DataManager 注入的位置似踱。

public class ScAppComponent implements AppComponent{

    private Provider<DataManager> provideManagerProvider;
    private MembersInjector<MainActivity> mainActivityMembersInjector;

    public ScAppComponent(Builder builder) {
        assert builder != null;
        initialize(builder);
    }

    private void initialize(final Builder builder){
        this.provideManagerProvider = (Provider<DataManager>) AppModule_ProvideManagerFactory.create(builder.appModule);
        this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideManagerProvider);
    }

    @Override
    public void inject(MainActivity activity) {
        mainActivityMembersInjector.injectMembers(activity);
    }

    public static Builder builder(){
        return new Builder();
    }

    public static AppComponent create(){
        return builder().build();
    }

    public static final class Builder{

        private AppModule appModule;

        private Builder(){}

        public AppComponent build(){
            if(appModule == null){
                this.appModule = new AppModule();
            }

            return new ScAppComponent(this);
        }

        public Builder appMoudle(AppModule appModule){
//            this.appModule = Preconditions.checkNotNull(appModule)
            return this;
        }

    }
}

  1. 調(diào)用 builder 方法時核芽,會返回一個內(nèi)部靜態(tài)類 Builder 的實例
public static Builder builder(){
    return new Builder();
}
  1. 內(nèi)部靜態(tài)類 Builder ,這是一般構(gòu)建者的模式的 Builder,然后調(diào)用 Builder 類的 build 方法看代碼會給我們返回一個 AppComponent 然后我們看代碼 build

    1. 先創(chuàng)建了一個 AppModule
    2. 然后將 Builder 自己作為參數(shù)傳入給 AppComponent 構(gòu)造函數(shù)創(chuàng)建一個 AppComponent 實例哮独。
  2. 在 AppComponent 構(gòu)造函數(shù)中會調(diào)用 initialize 方法

     private void initialize(final Builder builder){
        this.provideManagerProvider = (Provider<DataManager>) AppModule_ProvideManagerFactory.create(builder.appModule);
        this.mainActivityMembersInjector = MainActivity_MembersInjector.create(provideManagerProvider);
    }
    

在 initialize 方法中皮璧,會初始化

private Provider<DataManager> provideManagerProvider;
private MembersInjector<MainActivity> mainActivityMembersInjector;

這里我們在分別看一下這兩個 AppModule_ProvideManagerFactory 和 MainActivity_MembersInjector 他們倆也是通過 APT 生成悴务,所以我們一步一步寫一寫譬猫,來了解他們都做了什么,先看 AppModule_ProvideManagerFactory 叨恨,看名字應該是一個工廠類特碳,用于生成 DataManager 實例的吧晕换。

public final class AppModule_ProvideManagerFactory implements Factory<DataManager> {

    private final AppModule module;

    public AppModule_ProvideManagerFactory(AppModule module) {
        assert module != null;
        this.module = module;
    }

    @Override
    public DataManager get() {
        return module.provideManager();
    }

    public static Factory<DataManager> create(AppModule module){
        return new AppModule_ProvideManagerFactory(module);
    }
}

我們從 create 入手益愈,這里返回一個 Factory<DataManager> 類型的對象那么我們就簡單定義一下。Factory<T> 作為一個接口擴展了 Provider<T>接口

public interface Factory<T> extends Provider<T>{
}

所以定義一下Provider<T> 有一個方法 get 用于返回我們創(chuàng)建好的(要注入的)實例

public interface Provider<T> {
    T get();
}

好準備好了所需的接口,我們再回到 AppModule_ProvideManagerFactory 這需要實現(xiàn) Factory<T> 接口,用于提供產(chǎn)品的工廠吧
這里的 get 方法中是調(diào)用我們 module(AppModule)的 provideManager 方法來返回 DataManager 那么我們就需要有一個 AppModule 的實例蝶怔,實現(xiàn)起來也是一套

  1. create 創(chuàng)建自己然后 module 傳入
  2. module 作為 AppModule_ProvideManagerFactory 參數(shù)在構(gòu)造賦值
  3. 調(diào)用 get 實際就是調(diào)用 AppModule.provideManager

然后再看 MainActivity_MembersInjector 這個類,需要實現(xiàn) MembersInjector<T> 接口

public interface MembersInjector<T> {

    void injectMembers(T t);
}

public class MainActivity_MembersInjector implements MembersInjector<MainActivity> {

    private final Provider<DataManager> dataManagerProvider;

    public MainActivity_MembersInjector(Provider<DataManager> dataManagerProvider) {
        assert dataManagerProvider != null;
        this.dataManagerProvider = dataManagerProvider;
    }

    public static MembersInjector<MainActivity> create(Provider<DataManager> dataManagerProvider){
        return new MainActivity_MembersInjector(dataManagerProvider);
    }


    @Override
    public void injectMembers(MainActivity instance) {
        if(instance == null){
            throw new NullPointerException("空指針");
        }
        instance.dataManager = dataManagerProvider.get();

    }

    public static void injectDataManager(
            MainActivity instance, Provider<DataManager> dataManagerProvider
    ){
        instance.dataManager = dataManagerProvider.get();
    }
}

這里 create 需要傳入 Provider<DataManager> 的 dataManagerProvider 提供 DataManger 五督,調(diào)用 dataManagerProvider.get 方法就可以得到 DataManager 的實例讓然后在 injectDataManager 方法中將這個實例賦值給 MainActivity的dataManager 屬性來完成注入概荷。所以這個類需要一個 Provider<DataManager> 實例這是在構(gòu)造函數(shù)傳入的 create 的方式與上面的 AppModule_ProvideManagerFactory 的 create 套路相同,injectMembers(MainActivity instance) 是將 MainActivity 導入進入继薛,然后我們就可以輕松地將 provider 提供 DataManager 實例賦給 MainActivity 的屬性遏考。

3. 這里調(diào)用 inject方法是 ScAppComponent的 inject 方法 也就是
mainActivityMembersInjector 的 injectMembers 方法來將 MainActivity 傳入到類供注入

ScAppComponent.builder()
.appMoudle(new AppModule())
.build() //使用構(gòu)建者模式創(chuàng)建一個 Component 實例
.inject(this);


運行代碼

: DataManager: ininial

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末青团,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子督笆,更是在濱河造成了極大的恐慌,老刑警劉巖料扰,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件孔厉,死亡現(xiàn)場離奇詭異,居然都是意外死亡郑趁,警方通過查閱死者的電腦和手機舅柜,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來致份,“玉大人,你說我怎么就攤上這事。” “怎么了矫夯?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵旺订,是天一觀的道長弄企。 經(jīng)常有香客問我,道長区拳,這世上最難降的妖魔是什么拘领? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮樱调,結(jié)果婚禮上约素,老公的妹妹穿的比我還像新娘。我一直安慰自己笆凌,他們只是感情好圣猎,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著乞而,像睡著了一般送悔。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上爪模,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天欠啤,我揣著相機與錄音,去河邊找鬼屋灌。 笑死洁段,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的共郭。 我是一名探鬼主播祠丝,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼疾呻,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了纽疟?” 一聲冷哼從身側(cè)響起罐韩,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎污朽,沒想到半個月后散吵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡蟆肆,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年矾睦,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片炎功。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡枚冗,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蛇损,到底是詐尸還是另有隱情赁温,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布淤齐,位于F島的核電站股囊,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏更啄。R本人自食惡果不足惜稚疹,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祭务。 院中可真熱鬧内狗,春花似錦、人聲如沸义锥。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽拌倍。三九已至赂鲤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贰拿,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工熄云, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留膨更,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓缴允,卻偏偏與公主長得像荚守,于是被迫代替她去往敵國和親珍德。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容