一看就會禀梳,Dagger原理簡單分析

Dagger2是一款依賴注入框架,相比于黃油刀肠骆,Dagger2的入門難度系數(shù)更大算途。如果你還沒有使用過Dagger2,趕緊去嘗試一下吧蚀腿。

首先說一下Dagger2嘴瓤,可以幫我們干哪些活。使用過MVP開發(fā)的老鐵們都知道莉钙,Activity持有Presenter層的實例廓脆,也就是說 ,我們需要去實例化這個Presenter,而Presenter持有了View層接口的實例。這樣程序之間耦合性非常嚴(yán)重磁玉,我們希望停忿,把某個類的實例化過程放到一個容器中,當(dāng)前的Activity不需要去管理他的實例化過程蚊伞,說的直白點瞎嬉,不用我們手動的去new 這個對象了尉咕。我們仔細想想串前,這樣做有什么好處,假設(shè)程序中有很多的地方用到了這個Presenter曙旭,有朝一日别垮,我們發(fā)現(xiàn),Presenter設(shè)計的不夠完美便监,構(gòu)造方法需要改動,這樣一來可就麻煩了碳想,所有用到的Presenter的地方烧董,都需要去進行改動。這個時候胧奔,Dagger派上用場了

常規(guī)寫法:

public  class MainActivity extends AppCompatActivity implements IView{
    public MainPresenter mainPresenter;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv_1);
        // 實例化Presenter
        mainPresenter = new MainPresenter(this);
        mainPresenter.getData();
    }

    @Override
    public void getString(String str) {
        // Iview 接口的回調(diào)
        textView.setText(str);
    }
}

public interface IView {
    void  getString(String str);
}

public class MainPresenter {
    IView  iView;
    MainPresenter(IView iView) {
        this.iView = iView;
    }
    // 提供給 Activity層調(diào)用
    public void getData() {
       //  dosomething:假設(shè)通過Model層獲取到數(shù)據(jù)之后逊移,回調(diào)給IView。
        iView.getString("hello java");
    }
}

以上就是最簡單的MVP的寫法(沒有寫Model層)龙填。

使用Dagger怎么寫呢胳泉?

public  class MainActivity extends AppCompatActivity implements IView{

    @Inject
    public MainPresenter mainPresenter;
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = findViewById(R.id.tv_1);
        DaggerMainComponent.builder()
                .mainViewModule(new MainViewModule(this))
                .build()
                .inject(this);
        mainPresenter.getData();
    }

    @Override
    public void getString(String str) {
         // TODO
        textView.setText(str);
    }
}

public interface IView {
    void  getString(String str);
}

public class MainPresenter {
    IView  iView;
    @Inject
    MainPresenter(IView iView) {
        this.iView = iView;
    }

    public void getData() {
       //  dosomething:假設(shè)通過Model層獲取到數(shù)據(jù)之后,回調(diào)給IView岩遗。
        iView.getString("hello java");
    }
}

相比于常規(guī)寫法扇商,我們沒有在MainActivity里進行new MainPresenter(this)操作,而是使用了 @Inject 來標(biāo)注了我們想要實例化的類以及標(biāo)注它的構(gòu)造函數(shù)宿礁。使用@Inject相當(dāng)于做了個"記號"案铺,告訴Dagger,我想要這個類的實例,麻煩你去幫我做一下實例化操作梆靖。

這里再貼一下控汉,Component和Module的代碼:

### MainComponent

@Component (modules = {MainViewModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}
////////////////////////////////////////////
### MainViewModule
@Module
public class MainViewModule {
    IView iView;
    public MainViewModule(IView iView) {
        this.iView = iView;
    }
    @Provides
     IView create() {
        return iView;
    }
}

以上是Dagger2很簡單的使用笔诵,當(dāng)然這也不是我們今天所要關(guān)心的,今天的主題是姑子,Dagger2是怎么幫我們實現(xiàn)了 new MainPresenter(this) 這個操作的嗤放,內(nèi)部的邏輯是怎樣的呢?

簡單分析原理

Dagger2自動生成代碼的原理是 通過apt插件在編譯時期生成相應(yīng)的注入代碼,接下來我們就一個一個的分析壁酬。

1.MainPresenter與MainPresenter_Factory

我們用 @Inject 標(biāo)注了MainPresenter 以及他的 構(gòu)造函數(shù),然后我們Rebuild Project一下恨课,會在app/build/generated/source/apt/debug/包名/ 下生成一個 類 MainPresenter_Factory舆乔,這里貼一下 MainPresenter與MainPresenter_Factory 的代碼:

### MainPresenter_Factory類
public final class MainPresenter_Factory implements Factory<MainPresenter> {
  private final Provider<IView> iViewProvider;

  public MainPresenter_Factory(Provider<IView> iViewProvider) {  
    assert iViewProvider != null;
    this.iViewProvider = iViewProvider;
  }

  @Override
  public MainPresenter get() {  
    return new MainPresenter(iViewProvider.get());
  }

  public static Factory<MainPresenter> create(Provider<IView> iViewProvider) {  
    return new MainPresenter_Factory(iViewProvider);
  }
}

### MainPresenter類
public class MainPresenter {
    IView  iView;

    @Inject
    MainPresenter(IView iView) {
        this.iView = iView;
    }

    public void getData() {
       //  dosomething:假設(shè)通過Model層獲取到數(shù)據(jù)之后,回調(diào)給IView剂公。
        iView.getString("hello java");
    }
}

我們重點看MainPresenter_Factory 的get()方法希俩,返回類型是 MainPresenter,get()方法內(nèi)部實際上是去new了一個MainPresenter纲辽,傳入的參數(shù)是iViewProvider.get()颜武。我們不禁會想,Dagger2 是不是在這里幫我們實例化MainPresenter的拖吼?答案是肯定的

iViewProvider是一個Provider類型鳞上,泛型參數(shù)是我們 new MainPresenter()所需要的IView。iViewProvider是在MainPresenter_Factory 的構(gòu)造函數(shù)進行初始化的吊档。

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

create()返回返回一個 MainPresenter_Factory實例篙议,好像沒什么好說的,暫時先放一邊怠硼。

MainPresenter_Factory類看完了鬼贱,還是一頭霧水,我們知道get()方法會返回一個我們所需要的MainPresenter實例香璃,但是我們不清楚 get()是被誰調(diào)用的这难,以及iViewProvider.get() 是誰提供的呢?葡秒?

思考: MainPresenter 的構(gòu)造函數(shù)中傳入的參數(shù)是IView姻乓,而IView是接口類型,Dagger明確規(guī)定眯牧,不能使用@Inject來標(biāo)注接口和第三方庫糖权,碰到這種情況,需要使用到Module炸站,所以我們有理由相信星澳,iViewProvider.get() 是由MainViewModule提供的

帶著這兩個疑問看下文!旱易!!

2.MainViewModule與MainViewModule_CreateFactory

public final class MainViewModule_CreateFactory implements Factory<IView> {
  private final MainViewModule module;

  public MainViewModule_CreateFactory(MainViewModule module) {  
    assert module != null;
    this.module = module;
  }

  @Override
  public IView get() {  
    IView provided = module.create();
    if (provided == null) {
      throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method");
    }
    return provided;
  }

  public static Factory<IView> create(MainViewModule module) {  
    return new MainViewModule_CreateFactory(module);
  }
}


////////////////////////////////////////////

@Module
public class MainViewModule {
    IView iView;

    public MainViewModule(IView iView) {
        this.iView = iView;
    }
    @Provides
     IView create() {
        return iView;
    }

}

重點看看 MainViewModule_CreateFactory的get()方法禁偎,返回IView,這不就是我們new MainPresenter()所需要的參數(shù)嗎腿堤!但是到這里我們還是不能確定,iViewProvider.get() 是由MainViewModule提供的如暖!

我們雖然找到了MainViewModule_CreateFactory笆檀,也看到了它的get()方法的返回值就是我們 new MainPresenter()所需要的那個參數(shù),但是盒至,我們沒有找到證據(jù)酗洒,iViewProvider.get() 就是由MainViewModule提供的。

正當(dāng)我一籌莫展的時候枷遂,想起了之前網(wǎng)上看博客大牛們說樱衷,Component的作用像一個橋梁,負(fù)責(zé)把@Inject和Module的提供的實例一起注入到目標(biāo)類中.. 莫非酒唉,我要找的答案在MainComponent以及它生成的類中嗎矩桂?

答案也是肯定的!;韭住侄榴!

3.MainComponent與DaggerMainComponent

public final class DaggerMainComponent implements MainComponent {
  private Provider<IView> createProvider;
  private Provider<MainPresenter> mainPresenterProvider;
  private MembersInjector<MainActivity> mainActivityMembersInjector;

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

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

  private void initialize(final Builder builder) {  
    this.createProvider = MainViewModule_CreateFactory.create(builder.mainViewModule);
    this.mainPresenterProvider = MainPresenter_Factory.create(createProvider);
    this.mainActivityMembersInjector = MainActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), mainPresenterProvider);
  }

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

  public static final class Builder {
    private MainViewModule mainViewModule;
  
    private Builder() {  
    }
  
    public MainComponent build() {  
      if (mainViewModule == null) {
        throw new IllegalStateException("mainViewModule must be set");
      }
      return new DaggerMainComponent(this);
    }
  
    public Builder mainViewModule(MainViewModule mainViewModule) {  
      if (mainViewModule == null) {
        throw new NullPointerException("mainViewModule");
      }
      this.mainViewModule = mainViewModule;
      return this;
    }
  }
}

//////////////////////////////////
@Component (modules = {MainViewModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}

DaggerMainComponent實現(xiàn)了MainComponent接口。在構(gòu)造函數(shù)里調(diào)用了initialize()网沾,initialize()內(nèi)部做了初始化操作癞蚕。首先創(chuàng)造了MainViewModule_CreateFactory實例,接著把這個實例作為參數(shù)去生成MainPresenter_Factory實例辉哥,這不就是我們在第二步一直困惑的地方嗎涣达,終于找到證據(jù)了,哈哈。 MainPresent實例所需要的依賴由MainViewModule_CreateFactory提供证薇。最后一步是生成MainActivity_MembersInjector實例度苔,我猜測是MainActivity的注入類。

內(nèi)部類Builder主要是做了創(chuàng)建Module以及自身的實例浑度,不是重點寇窑。

我們大概捋一捋,我們所需要的MainPresenter實例由MainPresenter_Factory##get()創(chuàng)建箩张,而創(chuàng)建MainPresent實例所需要的依賴由MainViewModule_CreateFactory提供甩骏。但是,在步驟1里先慷,我們還是有個疑問沒解決饮笛,MainPresenter_Factory##get()是被誰調(diào)用的呢?

重點看看DaggerMainComponent###inject()方法论熙,方法內(nèi)部調(diào)用的是 mainActivityMembersInjector.injectMembers(activity)福青,將MainActivity注入到MainActivity_MembersInjector類中。

public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final MembersInjector<AppCompatActivity> supertypeInjector;
  private final Provider<MainPresenter> mainPresenterProvider;

  public MainActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<MainPresenter> mainPresenterProvider) {  
    assert supertypeInjector != null;
    this.supertypeInjector = supertypeInjector;
    assert mainPresenterProvider != null;
    this.mainPresenterProvider = mainPresenterProvider;
  }

  @Override
  public void injectMembers(MainActivity instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.mainPresenter = mainPresenterProvider.get();
  }

  public static MembersInjector<MainActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<MainPresenter> mainPresenterProvider) {  
      return new MainActivity_MembersInjector(supertypeInjector, mainPresenterProvider);
  }
}

MainActivity_MembersInjector構(gòu)造函數(shù)里接收一個Provider<MainPresenter>參數(shù),這個在injectMembers()里會用到无午。

我們單獨把injectMembers()拎出來:

public void injectMembers(MainActivity instance) {  
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    supertypeInjector.injectMembers(instance);
    instance.mainPresenter = mainPresenterProvider.get();
  }

instance.mainPresenter = mainPresenterProvider.get();
看到這行代碼的時候媒役,所有的疑問都消除了,也就是說 上面的那個疑問:MainPresenter_Factory##get()是被誰調(diào)用的呢宪迟?酣衷? 它其實是在這里被調(diào)用的。

看到這里是不是有種 恍然大悟的感覺次泽!mainPresenterProvider.get()也就是 Dagger2幫助我們生成的MainPresenter實例穿仪,剛開始我們還保持懷疑,mainPresenterProvider.get()得到到實例是不是指向了我們MainActivity的那個實例呢意荤? 看到這里啊片,才真正的明白了,將MainPresenter_Factory中創(chuàng)建好的MainPresenter實例賦值給instance(MainActivity)的成員mainPresenter袭异,這樣我們用@Inject標(biāo)注的mainPresenter就得到了實例化。

做個小結(jié)吧炬藤,Module的作用是 提供依賴御铃,像我們的例子中的:

MainPresenter(IView iView) {
       this.iView = iView;
}

這個IView 實例就是由Module去提供的,而Component的作用像一個橋梁沈矿,負(fù)責(zé)把@Inject和Module的提供的實例一起注入到目標(biāo)類中上真,像這個例子,目標(biāo)類就是MainActivity了羹膳。

Android的技術(shù)討論Q群:947460837

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末睡互,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子陵像,更是在濱河造成了極大的恐慌就珠,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件醒颖,死亡現(xiàn)場離奇詭異妻怎,居然都是意外死亡,警方通過查閱死者的電腦和手機泞歉,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門逼侦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人腰耙,你說我怎么就攤上這事榛丢。” “怎么了挺庞?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵晰赞,是天一觀的道長。 經(jīng)常有香客問我,道長宾肺,這世上最難降的妖魔是什么溯饵? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮锨用,結(jié)果婚禮上丰刊,老公的妹妹穿的比我還像新娘。我一直安慰自己增拥,他們只是感情好啄巧,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著掌栅,像睡著了一般秩仆。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上猾封,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天澄耍,我揣著相機與錄音,去河邊找鬼晌缘。 笑死齐莲,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的磷箕。 我是一名探鬼主播选酗,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼岳枷!你這毒婦竟也來了芒填?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤空繁,失蹤者是張志新(化名)和其女友劉穎殿衰,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體盛泡,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡播玖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了饭于。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜀踏。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖掰吕,靈堂內(nèi)的尸體忽然破棺而出果覆,到底是詐尸還是另有隱情,我是刑警寧澤殖熟,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布局待,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏钳榨。R本人自食惡果不足惜舰罚,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望薛耻。 院中可真熱鬧营罢,春花似錦、人聲如沸饼齿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽缕溉。三九已至考传,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間证鸥,已是汗流浹背僚楞。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枉层,地道東北人泉褐。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像返干,于是被迫代替她去往敵國和親兴枯。 傳聞我的和親對象是個殘疾皇子血淌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353