Dagger2 | 二操灿、入門(mén) - @Component

Dagger2 的核心是 @Component,用來(lái)管理依賴(lài)注入的細(xì)節(jié)泵督,充當(dāng)目標(biāo)類(lèi)和實(shí)例類(lèi)之間的中介趾盐。當(dāng)它發(fā)現(xiàn)目標(biāo)類(lèi)需要依賴(lài),就會(huì)自動(dòng)生成對(duì)應(yīng)的實(shí)例小腊,并注入到指定位置救鲤。

API 描述:

Annotates an interface or abstract class for which a fully-formed, dependency-injected implementation is to be generated from a set of modules. The generated class will have the name of the type annotated with @Component prepended with Dagger. For example, @Component interface MyComponent {...} will produce an implementation named DaggerMyComponent.

簡(jiǎn)單翻譯:

注解一個(gè)接口或抽象類(lèi),該接口或抽象類(lèi)將從 @Modules 集合生成完整的依賴(lài)注入實(shí)現(xiàn)秩冈。用 @Component 注解接口所生成的類(lèi)本缠,將有一個(gè)使用 Dagger 前綴的類(lèi)型名稱(chēng)。例如入问,@Component MyComponent{…} 將生成一個(gè)名為 DaggerMyComponent 的實(shí)現(xiàn)丹锹。

2.1 解決思路

上一章,依賴(lài)注入失敗芬失,我們需要排查問(wèn)題楣黍,找到解決辦法。

@Inject 引申出來(lái)的 FactoryMembersInjector 接口棱烂,需要重點(diǎn)關(guān)注租漂。

2.1.1 工廠接口

Factory 接口是 Dagger2 框架的成員,它繼承 JSR330 框架的 Provider 接口,用來(lái)提供實(shí)例窜锯。

API 描述:

An unscoped Provider. While a Provider may apply scoping semantics while providing an instance, a factory implementation is guaranteed to exercise the binding logic (@Inject constructors, @Provides methods) upon each call to get.
Note that while subsequent calls to get will create new instances for bindings such as those created by @Inject constructors, a new instance is not guaranteed by all bindings. For example, @Provides methods may be implemented in ways that return the same instance for each call.

簡(jiǎn)單翻譯:

一種非范圍提供者张肾。雖然 JSR330 框架的提供者接口可以在提供實(shí)例時(shí)應(yīng)用范圍語(yǔ)義(譯注:通常是指單例),但工廠實(shí)現(xiàn)可以保證在每次調(diào)用 get 方法時(shí)執(zhí)行綁定邏輯(即 @Inject 注解構(gòu)造函數(shù)锚扎,@Provides 注解方法)吞瞪。
注意:雖然 get 方法的后續(xù)調(diào)用將為綁定創(chuàng)建新的實(shí)例,比如由 @Inject 注解構(gòu)造函數(shù)所創(chuàng)建的那些實(shí)例驾孔,但并非所有綁定都保證創(chuàng)建新的實(shí)例芍秆。例如,@Provides 注解方法的實(shí)現(xiàn)方式可能為每次調(diào)用返回相同的實(shí)例翠勉。

提示:JSR330 框架的 Provider 接口不在本章的討論范圍內(nèi)妖啥,留給大家自行探索。Dagger2 框架的 @Provides 注解將在下一章開(kāi)始討論对碌。

Factory 接口的 API 描述和上一章的總結(jié)一致荆虱,由于它是正常工作,我們可以排除嫌疑朽们。

2.1.2 成員注入器接口

MembersInjetor 接口是 Dagger2 框架的成員怀读,它有一個(gè) injectMembers 方法,用來(lái)注入成員骑脱。

查看 injectMembers 方法的 API 描述:

Injects dependencies into the fields and methods of instance. Ignores the presence or absence of an injectable constructor.
Whenever a @Component creates an instance, it performs this injection automatically (after first performing constructor injection), so if you're able to let the component create all your objects for you, you'll never need to use this method.

簡(jiǎn)單翻譯:

注入依賴(lài)到實(shí)例的字段和方法上菜枷。忽略可注入構(gòu)造函數(shù)是否存在。
當(dāng)創(chuàng)建一個(gè) @Component 組件實(shí)例時(shí)叁丧,它會(huì)自動(dòng)執(zhí)行注入(在最先執(zhí)行的構(gòu)造函數(shù)注入之后)啤誊,所以如果你可以讓組件為你創(chuàng)建所有的對(duì)象,你永遠(yuǎn)不需要使用這個(gè)方法(譯注:指主動(dòng)調(diào)用此方法)拥娄。

原來(lái)是我們沒(méi)有用到 @Component蚊锹,所以依賴(lài)注入才沒(méi)有成功。

2.2 入門(mén)實(shí)戰(zhàn)

根據(jù) @Component 的 API 描述稚瘾,一步步實(shí)現(xiàn)依賴(lài)注入枫耳。

2.2.1 組件接口

創(chuàng)建 ActivityComponent 接口:

@Component
interface ActivityComponent {
}

編譯生成的 DaggerActivityComponent 類(lèi):

final class DaggerActivityComponent implements ActivityComponent {

  private DaggerActivityComponent() {
  }

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

  public static ActivityComponent create() {
    return new Builder().build();
  }

  static final class Builder {
    private Builder() {
    }

    public ActivityComponent build() {
      return new DaggerActivityComponent();
    }
  }
}

實(shí)現(xiàn)為建造者模式,可以通過(guò) new Builder().build()create() 方法獲取 ActivityComponent 實(shí)例孟抗,但并沒(méi)有生成依賴(lài)注入的相關(guān)代碼,我們還需要更多的資料钻心。

2.2.2 組件方法

繼續(xù)往下查看 @Component 的 API 描述:

Component methods
Every type annotated with @Component must contain at least one abstract component method. Component methods may have any name, but must have signatures that conform to either Provider or MembersInjector contracts.

簡(jiǎn)單翻譯:

組件方法
每個(gè)用 @Component 注解的類(lèi)型都必須包含至少一個(gè)抽象的組件方法凄硼。組件方法可以有任意名稱(chēng),但必須有符合 Provider 接口或 MembersInjector 接口約定的簽名捷沸。

Provision methods

Provision methods have no parameters and return an @Inject or @Provides type. Each method may have a @Qualifier annotation as well. The following are all valid provision method declarations:

 SomeType getSomeType();
 Set<SomeType> getSomeTypes();
 int getPortNumber();

簡(jiǎn)單翻譯:

提供方法沒(méi)有參數(shù)摊沉,返回 @Inject@Provides 類(lèi)型。每個(gè)方法上可以被 @Qualifier 注解痒给。以下是所有有效的提供方法聲明:

 SomeType getSomeType();
 Set<SomeType> getSomeTypes();
 int getPortNumber();

提示:@Qualifier 將在后面的章節(jié)中進(jìn)行介紹说墨。

按照以上描述骏全,在 ActivityComponent 組件中創(chuàng)建 account 方法:

@Component
public interface ActivityComponent {

    Account account();
}

編譯后,DaggerActivityComponent 類(lèi)出現(xiàn)新的內(nèi)容:

final class DaggerActivityComponent implements ActivityComponent {

    @Override
    public Account account() {
        return new Account();
    }

    // ...
}

還是直接 new 出來(lái) Account 類(lèi)的實(shí)例尼斧,提供方法不滿足我們的需求姜贡。

Members-injection methods

Members-injection methods have a single parameter and inject dependencies into each of the Inject-annotated fields and methods of the passed instance. A members-injection method may be void or return its single parameter as a convenience for chaining. The following are all valid members-injection method declarations:

 void injectSomeType(SomeType someType);
 SomeType injectAndReturnSomeType(SomeType someType);

簡(jiǎn)單翻譯:

成員注入方法只有一個(gè)參數(shù),它注入依賴(lài)項(xiàng)到此參數(shù)實(shí)例的每個(gè)被 @Inject 注解的字段和方法中棺棵。成員注入方法可以返回 void楼咳,也可以返回它傳入的單個(gè)參數(shù),以便進(jìn)行鏈?zhǔn)秸{(diào)用烛恤。下面是所有有效的成員注入方法聲明:

 void injectSomeType(SomeType someType);
 SomeType injectAndReturnSomeType(SomeType someType);

按照以上描述母怜,我們創(chuàng)建 inject 方法,參數(shù)為 MainActivity 類(lèi):

@Component
interface ActivityComponent {

    Account account();

    void inject(MainActivity activity);
}

編譯生成的關(guān)鍵內(nèi)容:

final class DaggerActivityComponent implements ActivityComponent {
  // ...

  @Override
  public Account account() {
    return new Account();}

  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);}

  private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectAccount(instance, new Account());
    return instance;
  }

  // ...
}

自動(dòng)調(diào)用成員注入器進(jìn)行 Account 實(shí)例的注入缚柏,現(xiàn)在完全滿足我們的需求苹熏。

注意,在 @Component 接口的成員注入方法中币喧,不能為了省事而只傳入通用基類(lèi)轨域。比方說(shuō):如果方法參數(shù)是 BaseActivity 類(lèi),那么 Dagger2 只會(huì)將依賴(lài)項(xiàng)注入到 BaseActivity 類(lèi)中被 @Inject 注解的地方粱锐,而不會(huì)對(duì)它的子類(lèi) MainActivity 進(jìn)行任何依賴(lài)注入疙挺,即便傳入的參數(shù)是子類(lèi) MainActivity 的實(shí)例。

2.2.3 簡(jiǎn)單測(cè)試

既然都是 new 出來(lái)的 Account 實(shí)例怜浅,那么在構(gòu)造函數(shù)上注解 @Inject 有點(diǎn)多余铐然。

讓我們注釋掉 @Inject,看看會(huì)出現(xiàn)什么情況:

錯(cuò)誤: [Dagger/MissingBinding] com.github.mrzhqiang.dagger2_example.account.Account cannot be provided without an @Inject constructor or an @Provides-annotated method.

原來(lái) Account 類(lèi)不能提供實(shí)例恶座,是因?yàn)閬G失 @Inject 注解的構(gòu)造函數(shù)或 @Provides 注解的方法搀暑。

由此可知,要提供實(shí)例跨琳,必須在 @Inject@Provides 中二選一自点,工廠接口的 API 描述也提到過(guò)這一點(diǎn)。關(guān)于 @Provides脉让,我們下一章再討論桂敛,現(xiàn)在 恢復(fù)構(gòu)造函數(shù)上的 @Inject,繼續(xù)下一步溅潜。

2.2.4 組件實(shí)例

構(gòu)建 ActivityComponent 接口的實(shí)例术唬,調(diào)用 inject 方法:

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import javax.inject.Inject;

public class MainActivity extends AppCompatActivity {

    @Inject
    Account account;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerActivityComponent.builder().build().inject(this);

        TextView contentText = findViewById(R.id.content_text);
        contentText.setText(account.toString());
    }
}

也可以這樣創(chuàng)建實(shí)例:

DaggerActivityComponent.create().inject(this);

最終,我們打通了依賴(lài)注入的全部流程滚澜,可以試著跑一下程序粗仓。

2.3 運(yùn)行

運(yùn)行效果:

依賴(lài)注入成功

沒(méi)有空指針異常,沒(méi)有閃退,成功顯示 Account 實(shí)例的內(nèi)容借浊。

2.4 總結(jié)

實(shí)現(xiàn) Dagger2 依賴(lài)注入的基本步驟:

  1. 創(chuàng)建實(shí)例類(lèi) Account 賬戶塘淑,將 @Inject 注解在構(gòu)造函數(shù)上
  2. 創(chuàng)建目標(biāo)類(lèi) MainActivity 活動(dòng),將 @Inject 注解在依賴(lài)字段上
  3. 創(chuàng)建組件 ActivityComponent 接口蚂斤,將 @Component 注解在接口上存捺,創(chuàng)建成員注入方法
  4. 構(gòu)建 ActivityComponent 接口的實(shí)例,在合適的地方調(diào)用成員注入方法

現(xiàn)在橡淆,我們已成功入門(mén) Dagger2 框架召噩,可以在實(shí)際開(kāi)發(fā)中大展身手。

下一章逸爵,討論 @Provides具滴,它可以實(shí)現(xiàn)第三方庫(kù)的依賴(lài)注入。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末师倔,一起剝皮案震驚了整個(gè)濱河市构韵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌趋艘,老刑警劉巖疲恢,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異瓷胧,居然都是意外死亡显拳,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)搓萧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)杂数,“玉大人,你說(shuō)我怎么就攤上這事瘸洛∽嵋疲” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵反肋,是天一觀的道長(zhǎng)那伐。 經(jīng)常有香客問(wèn)我,道長(zhǎng)石蔗,這世上最難降的妖魔是什么罕邀? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮养距,結(jié)果婚禮上诉探,老公的妹妹穿的比我還像新娘。我一直安慰自己铃在,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著定铜,像睡著了一般阳液。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上揣炕,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天帘皿,我揣著相機(jī)與錄音,去河邊找鬼畸陡。 笑死鹰溜,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的丁恭。 我是一名探鬼主播曹动,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼牲览!你這毒婦竟也來(lái)了墓陈?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤第献,失蹤者是張志新(化名)和其女友劉穎贡必,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體庸毫,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仔拟,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了飒赃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片利花。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖盒揉,靈堂內(nèi)的尸體忽然破棺而出晋被,到底是詐尸還是另有隱情,我是刑警寧澤刚盈,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布羡洛,位于F島的核電站,受9級(jí)特大地震影響藕漱,放射性物質(zhì)發(fā)生泄漏欲侮。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一肋联、第九天 我趴在偏房一處隱蔽的房頂上張望威蕉。 院中可真熱鬧,春花似錦橄仍、人聲如沸韧涨。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)虑粥。三九已至如孝,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娩贷,已是汗流浹背第晰。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留彬祖,地道東北人茁瘦。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像储笑,于是被迫代替她去往敵國(guó)和親甜熔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345