剖析Dagger2依賴注入

前言

本篇文章將結(jié)合我自己寫(xiě)的咖啡店賣(mài)咖啡的栗子的demo,結(jié)合代碼來(lái)分析一下Dagger2是如何來(lái)實(shí)現(xiàn)依賴注入的。
在我的第一篇文章中有提到如何配置Dagger2以及Dagger2的幾個(gè)關(guān)鍵概念,不清楚怎么配置的你,建議可以先看看我的第一篇文章,先配置好Dagger2再開(kāi)始學(xué)習(xí)下面的內(nèi)容,由于結(jié)合代碼,大部分代碼都貼出來(lái)的,篇幅較長(zhǎng),做好心理準(zhǔn)備,前方預(yù)警。

總是想著可以開(kāi)一家咖啡店,現(xiàn)在現(xiàn)實(shí)中開(kāi)不了,那就現(xiàn)在代碼里開(kāi)一家吧。程序員不僅能宅出一個(gè)世界,更重要的你能創(chuàng)建出一個(gè)你自己的世界,是不是突然自己好厲害! 好吧,不扯了,回到現(xiàn)實(shí)中,好好學(xué)習(xí)吧。
我先創(chuàng)建一家銷(xiāo)售咖啡的咖啡店,然后在里面買(mǎi)各種咖啡,黑咖啡呀等。既然是咖啡店,那肯定要有咖啡呀。所以咖啡就是我需要的一個(gè)對(duì)象。所以我的咖啡店還需要依賴于咖啡,沒(méi)咖啡,那怎么行,準(zhǔn)備好票子,來(lái)買(mǎi)我的咖啡吧。根據(jù)這個(gè)需求,我的咖啡店類(lèi)如下:

public class CoffeeShopActivity extends AppCompatActivity {
  // 1. 我要賣(mài)的咖啡對(duì)象,用inject注明要依賴的對(duì)象
  @Inject
  Coffee mCoffee ; 
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
      inject();
      operate();
  }
  //2.添加依賴關(guān)鍵代碼。
  private void inject() {
        DaggerCoffeeShopComponent.builder().build().inject(this);
 
  }

  private void operate() {
      mCoffee.getCoffee();
  }
}

Coffee是一個(gè)很簡(jiǎn)單的類(lèi),里面提供了一個(gè)getCoffee方法,打印了一串信息而已。

public class Coffee {
    private static final String TAG =     Coffee.class.getSimpleName();
    @Inject
    public Coffee(){

    }

    public void getCoffee() {
        Log.i(TAG, "black coffee");
    }
}

咖啡店里面是個(gè)很簡(jiǎn)單的依賴關(guān)系,CoffeeShopActivity依賴于類(lèi)Coffee,可以看到我們沒(méi)有用 new 來(lái)創(chuàng)建Coffee對(duì)象,而是直接通過(guò)@Inject來(lái)標(biāo)明CoffeeShopActivity的mCoffee需要依賴注入,來(lái)實(shí)例化這個(gè)變量。是不是很神奇,如果你知道了依賴注入是怎么注入的之后,你就不會(huì)覺(jué)得很神奇了。下面我們一層層地來(lái)剝開(kāi)這個(gè)“洋蔥”吧。

首先在上面代碼里,成員變量mCoffee定義的時(shí)候,添加了@Inject注釋?zhuān)缓驝offee的構(gòu)造函數(shù)添加了@Inject注釋。這樣就CoffeeShopActivity告訴了Dagger2,我需要Coffee的實(shí)例,然后Dagger2就去找到Coffee的構(gòu)造函數(shù)創(chuàng)建一個(gè)Coffee。難道就只要這兩個(gè)類(lèi)就可以了嗎,之前不是說(shuō)Component是目標(biāo)類(lèi)與被注入類(lèi)之間的橋梁?jiǎn)幔?Component呢? 沒(méi)錯(cuò),我還沒(méi)貼出Componet呢。自定義個(gè)Componet類(lèi)如下:

@Component
public interface CoffeeShopComponent  {
   void inject(CoffeeShopActivity coffeeShopActivity);
}

這個(gè)Component是不是很簡(jiǎn)單,只有一個(gè)抽象函數(shù),方法名是inject,方法名不是必須是inject,也可以是其他名字,但是參數(shù)一定要是你的目標(biāo)類(lèi),而且注意哦,不能是Activity,一定要是目標(biāo)類(lèi)自己的類(lèi)名。在第一篇文章里面就有提到過(guò),這里再?gòu)?qiáng)調(diào)一下,Dagger框架是通過(guò)參數(shù)的類(lèi)型以及返回值類(lèi)型,找到要依賴的類(lèi),從而建立聯(lián)系的,所以類(lèi)名一定要是直接要注入依賴的目標(biāo)類(lèi),返回值也是要注入的類(lèi)。

好了,注射器(Component)寫(xiě)好了,那要派上用場(chǎng)呀!那是在哪里用注射器把依賴注入了MainActivity呢。請(qǐng)看,我在MainActivity里面第2個(gè)注釋?zhuān)琲nject()方法。沒(méi)錯(cuò)就是這個(gè)方法,在這個(gè)方法里面用DaggerCoffeeShopComponent.builder().build().inject(this);來(lái)實(shí)現(xiàn)了注入。 等等,這個(gè)DaggerCoffeeShopComponet是哪里來(lái)的? 大家還記得在配置Dagger2框架環(huán)境的時(shí)候,引入了APT的包嗎,這個(gè)包是一個(gè)gradle插件,用于結(jié)合注釋自動(dòng)生成代碼,所以這個(gè)包一定要配置,否則不能自動(dòng)根據(jù)Dagger2的注釋來(lái)生成關(guān)鍵的代碼。想要了解APT的可以看看這篇文章android-apt。 所以DaggerCoffeeShopComponent就是一個(gè)自動(dòng)生成的實(shí)現(xiàn)了CoffeeShopComponent的類(lèi)。文件路徑是moudle路徑\build\generated\source\apt\debug\com\gotech\changelauncherbg\demodagger\DaggerCoffeeShopComponent,下面自動(dòng)生成的類(lèi)都在這個(gè)路徑里面。當(dāng)然APT還根據(jù)注釋自動(dòng)生成了其他的代碼。我們先分析這個(gè),因?yàn)檫@個(gè)是實(shí)現(xiàn)依賴注入的入口類(lèi)。注意啦,自動(dòng)生成代碼,是要你執(zhí)行了rebuild project才會(huì)自動(dòng)生成的,所以呢你編寫(xiě)完代碼之后記得一定要rebuild才能得到這些類(lèi)。

好吧,下面就一起來(lái)看看DaggerCoffeeShopComponent是怎么把Coffee的實(shí)例,注入到mCoffee里面的。上代碼:

//說(shuō)明這段代碼時(shí)生成的,并且生成自ComponetProcessor
@Generated("dagger.internal.codegen.ComponentProcessor")
public final class DaggerCoffeeShopComponent implements CoffeeShopComponent {
private MembersInjector<CoffeeShopActivity> coffeeShopActivityMembersInjector;

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

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

public static CoffeeShopComponent create() {  
  return builder().build();
}
// 初始化
private void initialize(final Builder builder) {  
  this.coffeeShopActivityMembersInjector = CoffeeShopActivity_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), Coffee_Factory.create());
}
//重寫(xiě)inject方法,實(shí)現(xiàn)依賴注入
@Override
public void inject(CoffeeShopActivity coffeeShopActivity) {  
  coffeeShopActivityMembersInjector.injectMembers(coffeeShopActivity);
}

public static final class Builder {
  private Builder() {  
  }

  public CoffeeShopComponent build() {  
    return new DaggerCoffeeShopComponent(this);
  }
}
}

從上面的代碼可以知道,DaggerCoffeeComponent用有一個(gè)內(nèi)部建造類(lèi)Builder,可以看成一個(gè)簡(jiǎn)單的建造模式的應(yīng)用。利用Builder來(lái)建造一個(gè)DaggerCoffeeShopComponet對(duì)象然后利用CoffeeShopActivity_MembersInjector初始化coffeeShopActivityMembersInjector。這個(gè)是真正實(shí)現(xiàn)依賴注入的類(lèi)。從類(lèi)名MembersInjector<CoffeeShopActivity> 可以看出這是一個(gè)用于注入成員變量的注入器,然后要注入的目標(biāo)類(lèi)是CoffeeShopActivity。 接下來(lái),我們看看CoffeeShopActivity_MembersInjector這個(gè)類(lèi)的代碼。

@Generated("dagger.internal.codegen.ComponentProcessor")
public final class CoffeeShopActivity_MembersInjector implements MembersInjector<CoffeeShopActivity> {
 private final MembersInjector<AppCompatActivity> supertypeInjector;
 private final Provider<Coffee> mCoffeeProvider;

 public CoffeeShopActivity_MembersInjector(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Coffee> mCoffeeProvider) {  
   assert supertypeInjector != null;
   this.supertypeInjector = supertypeInjector;
   assert mCoffeeProvider != null;
   this.mCoffeeProvider = mCoffeeProvider;
 }

 @Override
 public void injectMembers(CoffeeShopActivity instance) {  
   if (instance == null) {
     throw new NullPointerException("Cannot inject members into a null reference");
   }
   supertypeInjector.injectMembers(instance);
   //1.獲取一個(gè)Coffee的實(shí)例,復(fù)制給CoffeeShopActivity的實(shí)例instance,從這里可以看出mCoffee不能是私有的。
   instance.mCoffee = mCoffeeProvider.get();
 }

 public static MembersInjector<CoffeeShopActivity> create(MembersInjector<AppCompatActivity> supertypeInjector, Provider<Coffee> mCoffeeProvider) {  
     return new CoffeeShopActivity_MembersInjector(supertypeInjector, mCoffeeProvider);
 }
}

上面代碼注釋處,就是實(shí)現(xiàn)注入的地方,通過(guò)CoffeeProvider獲取一個(gè)Coffee對(duì)象,然后復(fù)制給CoffeeShopActivity對(duì)象的mCoffee成員變量。
跟蹤下來(lái),終于知道是在什么地方給要依賴的成員變量賦值的了。其實(shí)我們可以看出,并不是沒(méi)有用到new ,只不過(guò)Dagger2通過(guò)自動(dòng)生成代碼,幫你new了,你就省去了,這個(gè)new的過(guò)程,直接可以用了。這個(gè)呢,只是一個(gè)簡(jiǎn)單的成員變量的依賴跟蹤,其他的通過(guò)module來(lái)提供依賴對(duì)象的例子,其實(shí)跟蹤路徑和這個(gè)大同小異。 我這里就以小見(jiàn)大的分析這么個(gè)小例子,更復(fù)雜的用法如果有什么疑問(wèn),歡迎留言交流。

總結(jié)

我的咖啡店開(kāi)好了,咖啡都不用我自己去拿,自然有人給我送過(guò)來(lái),真是方便至極。干了這杯咖啡。最后,我做個(gè)簡(jiǎn)單的關(guān)鍵點(diǎn)總結(jié)吧!

  • 被依賴的成員變量的修飾符,不能是私有的。否則不能實(shí)現(xiàn)依賴注入。
  • 每次編寫(xiě)完代碼之后,記得rebuild project工程,自動(dòng)生成代碼。
  • 在使用成員變量之前,一定要先注入依賴,即編寫(xiě)一個(gè)類(lèi)似于CoffeeShopActivityinject方法。注入之后才能使用。
  • Component的接口里面的方法,參數(shù)類(lèi)型一定要是目標(biāo)類(lèi),不能是目標(biāo)類(lèi)的父類(lèi)。提供依賴的方法或者構(gòu)造函數(shù),返回的類(lèi)型也一定要是 要被依賴的類(lèi)的類(lèi)名,不能是父類(lèi)。

最后呢,雖然只是簡(jiǎn)單的一個(gè)成員變量的依賴注入的流程分析,但是其他復(fù)雜的依賴?yán)?,其?shí)看懂了這個(gè),再分析其他的應(yīng)該也挺簡(jiǎn)單的,關(guān)鍵是了解了原理。希望大家多多指點(diǎn),哪里寫(xiě)的不對(duì)的,或者文章編寫(xiě)有什么建議的,都?xì)g迎大家留言告知我。喜歡的在文末,輕輕點(diǎn)個(gè)贊哦。o 謝謝!Have a nice day!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,023評(píng)論 25 709
  • 前言 Dagger2 當(dāng)前Android流行的依賴注入框架。未來(lái)Android應(yīng)用的趨勢(shì)也許是MVP+Retrof...
    DorisSunny閱讀 1,428評(píng)論 0 3
  • 本文的分析基于dagger2的2.7版本。 谷歌開(kāi)發(fā)維護(hù)的Dagger2出來(lái)有很長(zhǎng)時(shí)間了,目前在很多開(kāi)源項(xiàng)目上也能...
    sososeen09閱讀 13,760評(píng)論 31 108
  • ****(說(shuō)在最前:閱讀本篇之前,希望大家對(duì)Dagger2已經(jīng)有了一個(gè)初步的了解。從而幫助感覺(jué)似是而非的同學(xué)進(jìn)一步...
    我是昵稱閱讀 1,029評(píng)論 3 6
  • 一直以來(lái),我有大男子主義,每次老婆說(shuō)我,我都是堅(jiān)定的否認(rèn)。但是夜深人靜時(shí),或者伏案寫(xiě)作時(shí),偶爾腦?;乇某觯夯蛟S我是...
    小李非刀閱讀 1,790評(píng)論 5 2

友情鏈接更多精彩內(nèi)容