1. 程式人生 > >Dagger2教程三之構造方法帶引數的情況(原)

Dagger2教程三之構造方法帶引數的情況(原)

        為了介紹Dagger2的使用,我們搭建了一個Demo來逐步分析,大家可以在這裡下載原始碼(https://github.com/dushaofeng/DaggerDemo.git)。
        前面一節介紹了Dagger2最簡單的使用場景,但是在結尾我們遇到兩個困難,即如何在不修改注入類程式碼的情況下實現注入?如何解決注入類構造方法需要引數的情況?
        比如我們有一個BeanNeedParam的物件,其構造方法中需要傳遞一個String型別的變數用來初始化mName
        public class BeanNeedParam {
            private String mName = null;


            public BeanNeedParam(String name) {
                this.mName = name;
            }


            public String getName() {
                return mName;
            }
        }
        接下來我們介紹如何在不修改這個類的前提下,如何將其注入到Activity中。

一、有引數的注入類使用方式

        為了解決有引數的問題,我們需要對之前的結構進行兩點改造,下面分別來進行。

1.1、改造Component

        首先我們來改造Component,我們依然使用上一篇中的demo,來將BeanComponent.java改造成如下方式:
        @Component(modules = BeanModule.class)
        public interface BeanComponent {
            void inject(MainActivity activity);
        }
        為了對比明顯,我們再貼出之前的BeanComponent內容:
        @Component
        public interface BeanComponent {
            void inject(MainActivity activity);
        }
        這個時候就能看出來,改造的唯一地方就是再Component的聲明後面多了一句:
        (modules = BeanModule.class)
        具體作用我們先不用管,稍後介紹,接下來我們再繼續進行第二個改造內容。

1.2、建立Dagger中的Module

        這個修改準確來說是建立一個新的檔案,也就是Module檔案。
        在上面1.1的對Component的改造中我們發現添加了modules = BeanModule.class的語句,這裡面的BeanModule就是我們要新增的檔案,檔名隨便起,只需要前後對應即可,我們這裡就用BeanModule來介紹:
        @Module
        public class BeanModule {
            @Provides
            public BeanNeedParam providerBean() {
                BeanNeedParam bean = new BeanNeedParam("BeanWithParam");
                return bean;
            }
        }
        這就是Module的全部內容,裡面有兩點需要特別注意:
        1、該檔案引入了@Module和@Provides兩個新的註釋
        2、在providerBean中完成了BeanNeedParam物件的建立工作,並傳遞了合適的引數

1.3、注入物件的使用

        經過以上兩點改造,我們就可以在Activity中直接使用BeanNeedParam物件了,這裡無需修改,繼續之前的使用即可:
        public class MainActivity extends AppCompatActivity {
            //帶引數的注入
            @Inject
            BeanNeedParam mBeanNeedParam;
            ......
            /*
            使用Dagger方式建立BeanForDagger物件
             */
            private void testDagger() {
                // 觸發Dagger機制
                DaggerBeanComponent.create().inject(this);
                if (mBeanNeedParam != null) {
                    Log.d(TAG, "mBeanNeedParam Name:" + mBeanNeedParam.getName());
                }
            }
        }
        這裡的使用方式和之前無需引數的注入方式完全相同。

二、注入過程介紹

        我們在之前無引數的注入過程基礎上來介紹有引數的情況下注入過程。
        通過之前的介紹,在Activity中通過DaggerBeanComponent.create().inject(this)觸發了Dagger的注入機制。
        然後Dagger掃描當前Activity有個BeanNeedParam的物件需要被注入:
        //帶引數的注入
        @Inject
        BeanNeedParam mBeanNeedParam;
        然後按照之前的介紹,Dagger將會在BeanNeedParam的類中搜索使用@Inject註釋了的構造方法,並用他來建立BeanNeedParam物件。
        但是這裡的BeanNeedParam中的構造方法沒有使用@Inject來註釋,說明無法直接注入。
        接下來Dagger就會在Component中搜索被註冊的modules有哪些,結果找到了BeanModule.class。
        然後在BeanModule.class中搜索同時具備如下條件的注入類:
        1、通過@Provides註釋的方法
        2、該方法必須是public屬性
        3、該方法的返回值是BeanNeedParam

        結果就找到了providerBean()方法,然後就呼叫該方法並順利拿到了BeanNeedParam物件,然後交給Activity的mBeanNeedParam使用。
        請注意,Dagger在Module中搜索目標類時,與Module中提供的方法名無關,只和返回值有關,比如下面兩種寫法是完全等效的,都可以實現BeanNeedParam的注入:
        @Provides
        public BeanNeedParam providerBean() {
            ......
        }
        和:
        @Provides
        public BeanNeedParam dushaofeng() {
            ......
        }
        整個過程可以歸納為以下步驟:
        1、Activity中通過DaggerXXXComponent的Inject()觸發注入過程
        2、Dagger在Activity中搜索用@Inject標註的變數,說明改物件需要被注入
        3、去Component中註冊的Module中搜索注入類
        4、在Module中搜索返回值為注入類的方法,執行並拿到注入類物件,從而完成注入過程
        5、如果在Module中沒有搜尋到提供目標類注入的方法,則在工程中搜索目標類
        6、找到需要注入物件後,尋找該物件中用@Inject標識的構造方法,完成自動建立過程

三、Module中方法需要引數的情況

        如果遇到Module中的方法需要引數的情況該如何處理呢?
        比如Module中提供BeanNeedParam的方法是這樣的:
        @Provides
        public BeanNeedParam providerBean(String name) {
            BeanNeedParam bean = new BeanNeedParam(name);
            return bean;
        }
        那麼Dagger在呼叫該方法時,就需要一個String型別的引數,如何將這個引數傳遞給Dagger呢?
        可以這樣實現,在Module中提供一個返回值為String的方法:
        @Module
        public class BeanModule {
            @Provides
            public BeanNeedParam providerBean(String name) {
                BeanNeedParam bean = new BeanNeedParam(name);
                return bean;
            }
            @Provides
            public String providerString(){
                return "param from other function";
            }
        }
        也就是說,Dagger在Module中執行providerBean時,發現需要一個String型別的引數,接著Dagger就會在Module中搜索返回值為String的方法並執行。
        這說明Dagger在Module中的執行也具備遞迴性。

四、一個疑問

        通過上面的介紹我們找到了不修改注入類程式碼的情況下將其注入的方法。但這種方法還是有個侷限:那就是每次注入只能傳遞給BeanNeedParam相同的引數,當我們需要給他傳遞不同的引數時,如何實現呢?
        比如對於BeanNeedParam的注入類來說,我們需要在不同場合傳遞給他不同的name值,該如何處理?
        這個問題下一篇來介紹。