Dagger2 中的 @Provides 是 @Inject 的替代方案。當(dāng)需要提供實(shí)例時(shí)嗦篱,由于 @Inject 注解在構(gòu)造函數(shù)上泣洞,因此無(wú)法提供第三方庫(kù)的實(shí)例,而 @Provides 則注解在方法上默色,可以通過(guò)方法返回第三方庫(kù)實(shí)例球凰。
API 描述:
Annotates methods of a
@Module
to create a provider method binding. The method's return type is bound to its returned value. The@Component
implementation will pass dependencies to the method as parameters.
簡(jiǎn)單翻譯:
@Module
注解在模塊方法上,用來(lái)創(chuàng)建提供者方法綁定腿宰。模塊方法的返回類(lèi)型綁定到它的返回值呕诉。@Component
的實(shí)現(xiàn)是將依賴(lài)項(xiàng)作為參數(shù)傳遞到模塊方法。
注意吃度,默認(rèn)情況下甩挫,Dagger2 禁止注入 null
值,調(diào)用 @Provides
注解的方法如果返回 null
值椿每,將立即拋出 NullPointerException
異常伊者。通過(guò) @Nullable
可以允許 null
值,但注入字段也必須用 @Nullable
注解间护,否則將編譯失敗亦渗。
3.1 進(jìn)階實(shí)戰(zhàn)
根據(jù) API 描述,我們先嘗試提供 Account
類(lèi)的實(shí)例汁尺,之后再用第三方庫(kù)提供實(shí)例法精。
3.1.1 賬戶(hù)模塊
查看 @Module
的 API 描述:
Annotates a class that contributes to the object graph.
簡(jiǎn)單翻譯:
注解在類(lèi)上,以便貢獻(xiàn)對(duì)象圖譜痴突。
創(chuàng)建 AccoutModule
類(lèi)搂蜓,添加 provideAccount
方法:
@Module
final class AccountModule {
@Provides
Account provideAccount() {
return new Account();
}
}
注意,你可以有任意命名辽装,但約定俗成的是帮碰,@Providers
注解的方法以 provide
作為前綴。
查看編譯生成的內(nèi)容:
public final class AccountModule_ProvideAccountFactory implements Factory<Account> {
// ...
@Override
public Account get() {
return provideAccount(module);
}
// ...
public static Account provideAccount(AccountModule instance) {
return Preconditions.checkNotNull(instance.provideAccount(), "Cannot return null from a non-@Nullable @Provides method");
}
}
恰好印證 Factory
接口的 API 描述拾积,即可以通過(guò) @Provides
注解的方法執(zhí)行綁定邏輯殉挽。
3.1.2 組件和模塊
回憶一下 @Component
的 API 描述:它可以從一組 @Module
集合中生成完整的依賴(lài)注入實(shí)現(xiàn)丰涉。
所以我們將模塊交給組件管理:
@Component(modules = AccountModule.class)
public interface ActivityComponent {
// ...
}
編譯后的內(nèi)容:
public final class DaggerActivityComponent implements ActivityComponent {
// ...
@Override
public Account account() {
return AccountModule_ProvideAccountFactory.provideAccount(accountModule);}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectAccount(instance, AccountModule_ProvideAccountFactory.provideAccount(accountModule));
return instance;
}
// ...
}
之前直接 new
實(shí)例的代碼消失,轉(zhuǎn)而調(diào)用 provideAccount
方法執(zhí)行注入此再。
我們注意到 ActivityComponent.Builder
類(lèi)的 build
方法:
public ActivityComponent build() {
if (accountModule == null) {
this.accountModule = new AccountModule();
}
return new DaggerActivityComponent(accountModule);
}
默認(rèn)情況下,模塊實(shí)例由建造者直接 new
出來(lái)玲销。
思考:如果模塊需要用 Context
進(jìn)行初始化输拇,該如何處理?
3.1.3 簡(jiǎn)單測(cè)試
我們依然對(duì)構(gòu)造函數(shù)上的 @Inject
抱有敵意贤斜,移除它之后策吠,再編譯一下:
Account_Factory
類(lèi)消失不見(jiàn),現(xiàn)在 Account
是 POJO 類(lèi)瘩绒。
3.1.4 第三方庫(kù)
我們用真正的第三方庫(kù)來(lái)提供依賴(lài)猴抹。
聲明依賴(lài)
在項(xiàng)目的 build.gradle
文件中,引入 jitpack.io
庫(kù):
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
在 app 模塊的 build.gradle
文件中锁荔,添加內(nèi)容:
dependencies {
// ...
implementation 'com.github.mrzhqiang:security:v1.0'
}
說(shuō)明一下蟀给,這個(gè)框架是從 spring-security-crypto 扒到 Android 上來(lái),用于信息的摘要加密處理阳堕。
為了運(yùn)行它跋理,我們還需要在 JDK1.8 版本上編譯,所以修改 app 模塊中的 build.gradle
文件:
android {
// ...
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
提供依賴(lài)
在賬戶(hù)模塊下提供依賴(lài):
@Module
final class AccountModule {
@Provides
Account provideAccount() {
return new Account();
}
@Provides
PasswordEncoder providePasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
編譯后恬总,生成新的類(lèi):
注入依賴(lài)
注入到 MainActivity
類(lèi)前普,對(duì)密碼進(jìn)行加密處理:
public class MainActivity extends AppCompatActivity {
@Inject
Account account;
@Inject
PasswordEncoder passwordEncoder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerActivityComponent.create().inject(this);
TextView contentText = findViewById(R.id.content_text);
String content = String.format("username: %s, password: %s, encodePassword: %s",
account.getUsername(),
account.getPassword(),
passwordEncoder.encode(account.getPassword()));
contentText.setText(content);
}
}
3.2 運(yùn)行
現(xiàn)在我們來(lái)運(yùn)行一下,看看效果:
即使是 123456 這樣的密碼壹堰,經(jīng)過(guò)加密處理后拭卿,也是普通暴力破解望而卻步的長(zhǎng)度和復(fù)雜度。
3.3 總結(jié)
@Provides
注解在提供者方法上贱纠,用來(lái)提供第三方庫(kù)依賴(lài)峻厚。
不僅如此,當(dāng)我們提取通用類(lèi)到公共模塊中谆焊,又希望公共模塊保持純潔目木,則可以通過(guò) @Provides
提供其依賴(lài)。更多情況下懊渡,創(chuàng)建實(shí)例之后還需要執(zhí)行初始化操作刽射,為此使用 @Provides
比 @Inject
更靈活。
下一章剃执,討論 @Singleton
誓禁,它可以幫助我們實(shí)現(xiàn)最佳單例模式。