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 namedDaggerMyComponent
.
簡(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)的 Factory
和 MembersInjector
接口棱烂,需要重點(diǎn)關(guān)注租漂。
2.1.1 工廠接口
Factory
接口是 Dagger2 框架的成員,它繼承 JSR330 框架的 Provider
接口,用來(lái)提供實(shí)例窜锯。
API 描述:
An unscoped
Provider
. While aProvider
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 toget
.
Note that while subsequent calls toget
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 eitherProvider
orMembersInjector
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)行效果:
沒(méi)有空指針異常,沒(méi)有閃退,成功顯示 Account
實(shí)例的內(nèi)容借浊。
2.4 總結(jié)
實(shí)現(xiàn) Dagger2 依賴(lài)注入的基本步驟:
- 創(chuàng)建實(shí)例類(lèi)
Account
賬戶塘淑,將@Inject
注解在構(gòu)造函數(shù)上 - 創(chuàng)建目標(biāo)類(lèi)
MainActivity
活動(dòng),將@Inject
注解在依賴(lài)字段上 - 創(chuàng)建組件
ActivityComponent
接口蚂斤,將@Component
注解在接口上存捺,創(chuàng)建成員注入方法 - 構(gòu)建
ActivityComponent
接口的實(shí)例,在合適的地方調(diào)用成員注入方法
現(xiàn)在橡淆,我們已成功入門(mén) Dagger2 框架召噩,可以在實(shí)際開(kāi)發(fā)中大展身手。
下一章逸爵,討論 @Provides
具滴,它可以實(shí)現(xiàn)第三方庫(kù)的依賴(lài)注入。