Android單排上王者系列之Dagger2注入原理解析

本篇文章已授權(quán)微信公眾號(hào) guolin_blog (郭霖)獨(dú)家發(fā)布

MVP模式講解
在MVP中使用Dagger2
Dagger2的注入原理解析

在上篇博客中我們介紹了Dagger2該如何在項(xiàng)目中使用啤呼,這篇博客將繼續(xù)分析Dagger2實(shí)現(xiàn)的原理,代碼依然采用上篇的代碼,看這里贡定。

Dagger2的注入原理

原理的講解我們通過小明來(lái)帶我們學(xué)習(xí)憎账。
小明在看了MVP的實(shí)戰(zhàn)解析和Dagger2的使用后知道了Dagger2該如何在MVP模式中使用,但是小明是一個(gè)要求上進(jìn)的好同學(xué)晨炕,小明并不滿足于如何使用蒸播,小明想鉆研鉆研源碼,看看如何實(shí)現(xiàn)的场航。小明在鉆研Dagger2的時(shí)候突然意識(shí)到Dagger2是采用注解的形式完成任務(wù)的缠导,使用注解其實(shí)是不明智的選擇,會(huì)大大消耗性能溉痢,影響應(yīng)用的運(yùn)行速度僻造。
小明看到這里有點(diǎn)疑惑了,既然注解這么影響性能孩饼,那為什么Dagger2還要使用注解呢髓削?為什么Dagger2還這么被廣泛的使用呢?
于是小明到github上查看Dagger2的介紹
官方介紹是

A fast dependency injector for Android and Java.
Dagger2是一個(gè)Android和Java中的快速注射器镀娶。

小明又疑惑了注解反射怎么是快速的呢立膛?小明沒有灰心又繼續(xù)查看代碼,終于發(fā)現(xiàn)Dagger2是和其他依賴注入框架是有區(qū)別的梯码,Dagger2是通過apt插件在編譯階段生成注入代碼的宝泵,也就是說(shuō)反射只是在編譯階段使用了,而在應(yīng)用運(yùn)行的時(shí)候其實(shí)運(yùn)行的是真正的Java代碼并沒有涉及到注解反射轩娶,小明終于明白了儿奶,難怪Dagger2是快速注入框架。
小明有了這個(gè)重大發(fā)現(xiàn)后決定一鼓作氣把Dagger2生成的代碼給理清楚罢坝。

編譯階段生成代碼

小明通過在Android studio中通過執(zhí)行Build->Rebuild Project廓握,在app/build/generated/source/apt目錄下發(fā)現(xiàn)生成了

LoginPresenterComp_Factory類

代碼如下

public final class LoginPresenterCompl_Factory implements Factory<LoginPresenterCompl> { 
        private final Provider<ILoginView> viewProvider;  
        public LoginPresenterCompl_Factory(Provider<ILoginView> viewProvider) { 
                assert viewProvider != null; 
                this.viewProvider = viewProvider; 
        }
        @Override 
        public LoginPresenterCompl get() { 
                return new LoginPresenterCompl(viewProvider.get()); 
        }  
        public static Factory<LoginPresenterCompl> create(Provider<ILoginView> viewProvider) {
                 return new LoginPresenterCompl_Factory(viewProvider); 
        }
}

為了對(duì)比小明又把LoginPresenterCompl的代碼也找了出來(lái)代碼如下

public class LoginPresenterCompl implements ILoginPresenter { 
        @Inject 
        public LoginPresenterCompl(ILoginView view){ 
                loginView = view ; 
                user = new User("張三","123456") ; 
        }
         ......
}

仔細(xì)看看LoginPresenterCompl_Factory這個(gè)類,發(fā)現(xiàn)其中有三個(gè)方法

  • 構(gòu)造方法

構(gòu)造方法中的參數(shù)viewProvider一個(gè)Provider類型的嘁酿,而Provider的泛型參數(shù)是ILoginView隙券,這個(gè)參數(shù)就是我們實(shí)例化LoginPresenterCompl需要的參數(shù),在上篇中我們知道了該參數(shù)是一個(gè)依賴闹司,是由MainModule提供的娱仔。

  • get()方法

在該方法中初始化了我們正在需要的LoginPresenterCompl對(duì)象

  • create()方法

在該方法中實(shí)例化了LoginPresenterCompl_Factory本類對(duì)象

小明想既然上面的viewProvider是由MainModule提供的,那么就來(lái)看看MainModule對(duì)應(yīng)的注入類吧

MainModule_ProvideILogViewFactory代碼如下

public final class MainModule_ProvideILogViewFactory implements Factory<ILoginView> { 
        private final MainModule module; 
        public MainModule_ProvideILogViewFactory(MainModule module) { 
                assert module != null; this.module = module; 
        } 
        @Override 
        public ILoginView get() { 
                return Preconditions.checkNotNull( module.provideILogView(), "Cannot return null from a non-@Nullable @Provides method"); 
        } 
        public static Factory<ILoginView> create(MainModule module) {
                return new MainModule_ProvideILogViewFactory(module); 
        }
}

對(duì)應(yīng)的MainModule代碼如下

@Modulepublic 
class MainModule { 
        private final ILoginView view ; 
        public MainModule(ILoginView view){ 
                this.view = view ; 
        } 
        @Provides 
        ILoginView provideILogView(){ 
                return view ; 
        }
}

從結(jié)構(gòu)中不難看出被@Provider注解修飾的方法會(huì)對(duì)應(yīng)的生成Factory類游桩,這個(gè)類中最主要的方法是get()方法牲迫,在該方法中調(diào)用了MainModule的provideILogView方法,而該方法是為了我們提供LoginPresenterCompl實(shí)例化參數(shù)的借卧,LoginPresenterCompl的實(shí)例化是在LoginPresenterCompl_Factory的get()方法中完成的盹憎。實(shí)例化代碼如下

@Override
public LoginPresenterCompl get() { 
        return new LoginPresenterCompl(viewProvider.get());
}

在代碼中可以看出實(shí)例化過程中參數(shù)是由viewProvider.get()提供的。咦n砹酢E忝俊!!
在MainModule_ProvideILogViewFactory中的get()方法其實(shí)返回了我們實(shí)例化的參數(shù)檩禾。那么這個(gè)viewProvider是不是我們的MainModule_ProvideILogViewFactory呢挂签?
viewProvider是一個(gè)Provider類型,而MainModule_ProvideILogViewFactory實(shí)現(xiàn)了Factory接口盼产,那Provider和Factory有沒有聯(lián)系呢饵婆?
看這段代碼

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

發(fā)現(xiàn)Factory接口繼承了Provider接口,所以其實(shí)viewProvider就是MainModule_ProvideILogViewFactory類型戏售∏群耍看到這里小明終于明白了LoginPresenterCompl_Factory類和MainModule_ProvideILoginViewFactory類的關(guān)系了,也明白了實(shí)例化過程了蜈项。但是這些類的初始化和相關(guān)方法是如何被調(diào)用的芹关,在哪里被調(diào)用的呢?
還有兩個(gè)重要的類小明沒有看到紧卒。MainComponent和對(duì)應(yīng)的DaggerMainComponent侥衬。
代碼如下
MainComponent代碼

@Component(modules = MainModule.class)
public interface MainComponent { 
        public void inject(LoginActivity activity) ;
}

DaggerMainComponent代碼

public final class DaggerMainComponent implements MainComponent { 
        private Provider<ILoginView> provideILogViewProvider; 
        private Provider<LoginPresenterCompl> loginPresenterComplProvider; 
        private MembersInjector<LoginActivity> loginActivityMembersInjector; 
        private DaggerMainComponent(Builder builder) { 
                assert builder != null; 
                initialize(builder); 
        } 
        public static Builder builder() { 
                return new Builder(); 
        } 
        @SuppressWarnings("unchecked") 
        private void initialize(final Builder builder) { 
                this.provideILogViewProvider = MainModule_ProvideILogViewFactory.create(builder.mainModule);
                 this.loginPresenterComplProvider = LoginPresenterCompl_Factory.create(provideILogViewProvider); 
                this.loginActivityMembersInjector = LoginActivity_MembersInjector.create(loginPresenterComplProvider); 
        } 
        @Override 
        public void inject(LoginActivity activity) { 
                loginActivityMembersInjector.injectMembers(activity); 
        } 
        public static final class Builder { 
                private MainModule mainModule; 
                private Builder() {} 
                public MainComponent build() { 
                        if (mainModule == null) { 
                                throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set"); 
                        } 
                        return new DaggerMainComponent(this); 
                } 
                public Builder mainModule(MainModule mainModule) { 
                        this.mainModule = Preconditions.checkNotNull(mainModule); return this; 
                } 
        }
}

通過上面代碼可以看出DaggerMainComponent實(shí)現(xiàn)了MainComponent接口并實(shí)現(xiàn)了其中的inject()方法。同時(shí)也提供了其他的輔助方法跑芳。小明決定從方法調(diào)用順序開始入手查看

DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this);

注入過程

還記得在LoginActivity中添加的這個(gè)方法嗎轴总,分析DaggerMainComponent就從這段代碼入手。

  • 1博个、DaggerMainComponent調(diào)用了builder方法
    小明找到builder()方法看看這個(gè)方法到底做了什么事
public static Builder builder() { return new Builder();}

發(fā)現(xiàn)這個(gè)方法創(chuàng)建并返回了Builder對(duì)象Builder是什么東東呢怀樟?仔細(xì)看代碼Build是DaggerMainCompone的內(nèi)部類。

  • 2盆佣、DaggerMainComponent.builder().mainModule(new MainModule(this))
    緊接著又調(diào)用了mainModule并將MainModule的對(duì)象傳了進(jìn)來(lái)往堡。mainModule()方法是Builder中的一個(gè)方法,代碼如下
public Builder mainModule(MainModule mainModule) { this.mainModule = Preconditions.checkNotNull(mainModule); return this;}

其中做了什么事呢共耍?就是將傳進(jìn)來(lái)的MainModule對(duì)象賦值給本類的mainModule對(duì)象虑灰,并返回本類對(duì)象

  • 3、DaggerMainComponent.builder().mainModule(new MainModule(this)).build()
    緊接著又調(diào)用了Builder的build()方法
public MainComponent build() { if (mainModule == null) { 
throw new IllegalStateException(MainModule.class.getCanonicalName() + " must be set"); 
} 
return new DaggerMainComponent(this);
}

該方法通過new DaggerMainComponent(this)創(chuàng)建了DaggerMainComponent對(duì)象并將其返回痹兜。那么new DaggerMainComponent(this)做了什么事呢穆咐?

private DaggerMainComponent(Builder builder) { assert builder != null; initialize(builder);}

其中調(diào)用了initialize方法并將builder對(duì)象傳入。initialize()方法如下

private void initialize(final Builder builder) { 
        this.provideILogViewProvider = MainModule_ProvideILogViewFactory.create(builder.mainModule); 
        this.loginPresenterComplProvider = LoginPresenterCompl_Factory.create(provideILogViewProvider); 
        this.loginActivityMembersInjector = LoginActivity_MembersInjector.create(loginPresenterComplProvider);
}

在initialize方法中小明終于看到了MainModule_ProvideILogViewFactory和LoginPresenterCompl_Factory的create方法被調(diào)用了字旭。
首先通過傳入的MainModule對(duì)象創(chuàng)建MainModule_ProvideILogViewFactory對(duì)象provideILogViewProvider对湃,然后將provideILogViewProvider對(duì)象作為參數(shù)來(lái)創(chuàng)建LoginPresenterCompl_Factory對(duì)象。
前面已經(jīng)講過遗淳,MainModule_ProvideILogViewFactory是一個(gè)Factory對(duì)象拍柒,而LoginPresenterCompl_Factory創(chuàng)建對(duì)象需要一個(gè)Provider對(duì)象,同時(shí)Factory繼承了Provider屈暗,所以可以將其傳入拆讯。所以LoginPresenterComp_Factory的viewProvider對(duì)象是一個(gè)MainModule_ProvideILogViewFactory對(duì)象剧包,這個(gè)概念前面也講過,這里得到認(rèn)證往果。

在這段代碼中小明發(fā)現(xiàn)了LoginActivity_MembersInjector類,于是小明又將這個(gè)類找了出來(lái)代碼如下

public final class LoginActivity_MembersInjector implements MembersInjector<LoginActivity> {
        private final Provider<LoginPresenterCompl> loginPresenterProvider; 
        public LoginActivity_MembersInjector(Provider<LoginPresenterCompl> loginPresenterProvider) { 
                assert loginPresenterProvider != null; 
                this.loginPresenterProvider = loginPresenterProvider; 
        } 
        public static MembersInjector<LoginActivity> create( Provider<LoginPresenterCompl> loginPresenterProvider) { 
                return new LoginActivity_MembersInjector(loginPresenterProvider); 
        } 
        @Override 
        public void injectMembers(LoginActivity instance) { 
        if (instance == null) { 
                throw new NullPointerException("Cannot inject members into a null reference"); 
        } 
        instance.loginPresenter = loginPresenterProvider.get(); 
        } 
        public static void injectLoginPresenter( LoginActivity instance, Provider<LoginPresenterCompl> loginPresenterProvider) { 
                instance.loginPresenter = loginPresenterProvider.get(); 
        }
}

該類的cereate()方法需要一個(gè)Provider泛型是LoginPresenterCompl類型的參數(shù)一铅,通過構(gòu)造函數(shù)將其傳入賦值給loginPresenterProvider變量陕贮。就這么簡(jiǎn)單。

  • 4潘飘、DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this)
    最后調(diào)用了inject()方法
@Override
public void inject(LoginActivity activity) { 
        loginActivityMembersInjector.injectMembers(activity);
}

在該方法中調(diào)用了LoginActivityMembersInjector中的injectMembers()方法肮之。injectMembers()方法內(nèi)容如下

@Override
public void injectMembers(LoginActivity instance) { 
        if (instance == null) { 
                throw new NullPointerException("Cannot inject members into a null reference"); 
        } 
        instance.loginPresenter = loginPresenterProvider.get();
}

終于!2仿肌8昵堋!<瓒尽?鸶摺!在這個(gè)方法中實(shí)現(xiàn)了對(duì)LoginPresenterCompl對(duì)象的初始化丑瞧。
至此柑土,小明終于弄清楚了Dagger2的注入原理了,小明表示清楚原理后媽媽再也不用擔(dān)心Dagger2寫錯(cuò)了绊汹。
如果沒有正確的分析這個(gè)生成的注入類可能很難理解Dagger2實(shí)現(xiàn)注入的框架稽屏,可能看原理代碼讓有些同學(xué)不知所措,相信我西乖,多分析幾遍就OK了狐榔。
其實(shí)也不用糾結(jié)到底該如何使用Dagger2,只要我們理解了其實(shí)現(xiàn)的原理获雕,具體如何使用看個(gè)人薄腻,能夠做到靈活使用就OK了。
至此Dagger2的原理分析就完成了典鸡。

總結(jié)

回顧一下該系列的文章
MVP模式講解
在MVP中使用Dagger2
Dagger2的注入原理解析因?yàn)檫@三篇是連續(xù)的被廓,代碼都是在前一篇的基礎(chǔ)上做的擴(kuò)展,所以最好將三篇博客通讀萝玷。希望這三篇文章能夠幫到需要的同學(xué)嫁乘,共同進(jìn)步!G虻铩蜓斧!
最后全部代碼點(diǎn)擊這里

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市睁冬,隨后出現(xiàn)的幾起案子挎春,更是在濱河造成了極大的恐慌看疙,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,509評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件直奋,死亡現(xiàn)場(chǎng)離奇詭異能庆,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)脚线,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,806評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門搁胆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人邮绿,你說(shuō)我怎么就攤上這事渠旁。” “怎么了船逮?”我有些...
    開封第一講書人閱讀 163,875評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵顾腊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我挖胃,道長(zhǎng)杂靶,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,441評(píng)論 1 293
  • 正文 為了忘掉前任冠骄,我火速辦了婚禮伪煤,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘凛辣。我一直安慰自己抱既,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,488評(píng)論 6 392
  • 文/花漫 我一把揭開白布扁誓。 她就那樣靜靜地躺著防泵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝗敢。 梳的紋絲不亂的頭發(fā)上捷泞,一...
    開封第一講書人閱讀 51,365評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音寿谴,去河邊找鬼锁右。 笑死,一個(gè)胖子當(dāng)著我的面吹牛讶泰,可吹牛的內(nèi)容都是我干的咏瑟。 我是一名探鬼主播,決...
    沈念sama閱讀 40,190評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼痪署,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼码泞!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起狼犯,我...
    開封第一講書人閱讀 39,062評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤余寥,失蹤者是張志新(化名)和其女友劉穎领铐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宋舷,經(jīng)...
    沈念sama閱讀 45,500評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绪撵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,706評(píng)論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了祝蝠。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片莲兢。...
    茶點(diǎn)故事閱讀 39,834評(píng)論 1 347
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖续膳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情收班,我是刑警寧澤坟岔,帶...
    沈念sama閱讀 35,559評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站摔桦,受9級(jí)特大地震影響社付,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜邻耕,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,167評(píng)論 3 328
  • 文/蒙蒙 一鸥咖、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧兄世,春花似錦啼辣、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,779評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至削解,卻和暖如春富弦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背氛驮。 一陣腳步聲響...
    開封第一講書人閱讀 32,912評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工腕柜, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人矫废。 一個(gè)月前我還...
    沈念sama閱讀 47,958評(píng)論 2 370
  • 正文 我出身青樓盏缤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親磷脯。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蛾找,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,779評(píng)論 2 354

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