在上一章,@Component
将项目中的类实例直接 new
出来,注入给依赖的地方。
对于第三方库,没法用 @Inject
标记,但 Dagger2
告诉我们可以 提供依赖。
3.1 提供依赖
根据 @Provides
的 API
描述,我们需要用 @Module
标记一个类,然后使用 @Provides
标记返回类型。
3.1.1 结构重构
在尝试之前,我们需要做一点小小的结构重构:
-java
-com.domain.project
-account
*Account
-di
*ActivityComponent
*MainAcitvity
现在,我们专注于 com.domain.project.di
包,开始我们全新的 dependencies inject
之旅。
3.1.2 依赖模块
首先在 com.domian.project.di
包下,创建 AccoutModule.java
类:
import dagger.Module;
@Module final class AccountModule {
}
从 OOP
角度来设计,我们不准备对外暴露这个模块,并且不允许它被扩展。
然后添加提供者方法:
@Module final class AccountModule {
@Provides Account provideAccount() {
return new Account();
}
}
通常我们使用 new
提供依赖,在更广阔的应用场景,Dagger2
还有妙招,暂且不提。
再来查看生成的内容:
import com.github.mrzhqiang.dagger2_example.account.Account;
import dagger.internal.Factory;
import dagger.internal.Preconditions;
@SuppressWarnings({
"unchecked",
"rawtypes"
})
public final class AccountModule_ProvideAccountFactory implements Factory<Account> {
private final AccountModule module;
public AccountModule_ProvideAccountFactory(AccountModule module) {
this.module = module;
}
@Override
public Account get() {
return provideAccount(module);
}
public static AccountModule_ProvideAccountFactory create(AccountModule module) {
return new AccountModule_ProvideAccountFactory(module);
}
public static Account provideAccount(AccountModule instance) {
return Preconditions.checkNotNull(instance.provideAccount(), "Cannot return null from a non-@Nullable @Provides method");
}
}
本质上还是一个 Factory<T>
的实现,只不过依赖来自于账户模块。
3.1.3 模块管理
因为组件是 Dagger2
的核心,所以模块将交给 @Component
管理:
@Component(modules = AccountModule.class)
public interface ActivityComponent {
void inject(MainActivity activity);
}
编译后的内容:
import com.github.mrzhqiang.dagger2_example.MainActivity;
import com.github.mrzhqiang.dagger2_example.MainActivity_MembersInjector;
import dagger.internal.Preconditions;
@SuppressWarnings({
"unchecked",
"rawtypes"
})
public final class DaggerActivityComponent implements ActivityComponent {
private final AccountModule accountModule;
private DaggerActivityComponent(AccountModule accountModuleParam) {
this.accountModule = accountModuleParam;
}
public static Builder builder() {
return new Builder();
}
public static ActivityComponent create() {
return new Builder().build();
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectAccount(instance, AccountModule_ProvideAccountFactory.provideAccount(accountModule));
return instance;
}
public static final class Builder {
private AccountModule accountModule;
private Builder() {
}
public Builder accountModule(AccountModule accountModule) {
this.accountModule = Preconditions.checkNotNull(accountModule);
return this;
}
public ActivityComponent build() {
if (accountModule == null) {
this.accountModule = new AccountModule();
}
return new DaggerActivityComponent(accountModule);
}
}
}
之前的 new
实例代码不见了,转而用 提供依赖 的方式。
思考:默认情况下,建造者模式会自动创建模块实例,如果从外部提供的话,将直接使用外部提供的模块实例。在这种情况下,模块是否可扩展将变得十分重要,这也是从一开始禁止模块扩展的原因。但我们也可以在不修改原有代码的前提下,进行依赖的迁移,前提是我们 面向接口编程。
public ActivityComponent build() {
if (accountModule == null) {
this.accountModule = new AccountModule();
}
return new DaggerActivityComponent(accountModule);
}
3.1.4 移除注入
现在,我们试着移除构造函数上的 @Inject
,再编译一下:
可以看到 Account
的工厂类已经消失,现在它是一个纯粹的 POJO
类。
3.2 第三方依赖
前面的内容不能完整描述提供第三方依赖的过程,所以我们可以再试一次。
3.2.1 神奇的依赖库
在项目的 build.gradle
文件中,引入 jitpack.io
依赖库:
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
这是一个可以直接提供 Git
开源项目依赖的 maven
库,不需要经历漫长的 Maven
中央库审核、上传、同步以及发布的过程,在项目初期的快速开发阶段,它的帮助非常大。
当然,如果你通过 nexus3
创建自己的 Maven
库,那就忽略我所说的这句话。
3.2.2 添加依赖
在 app
模块的 build.gradle
文件中,加入内容:
implementation 'com.github.mrzhqiang:security:v1.0'
说明一下,这个库是从 spring-security-crypto 扒到 Android
上来,用于信息的加密处理。
为了运行这个库,我们还需要在 JDK1.8
版本上编译:
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
3.2.3 提供依赖
好了,我们可以提供这个库的依赖给账户模块:
import cn.mrzhqiang.security.crypto.factory.PasswordEncoderFactories;
import cn.mrzhqiang.security.crypto.password.PasswordEncoder;
import com.github.mrzhqiang.dagger2_example.account.Account;
import dagger.Module;
import dagger.Provides;
@Module final class AccountModule {
@Provides Account provideAccount() {
return new Account();
}
@Provides PasswordEncoder providePasswordEncoder() {
return PasswordEncoderFactories.createDelegatingPasswordEncoder();
}
}
编译后,在 di
包下生成一个新的提供者工厂:
3.2.4 注入依赖
再注入到 MainActivity
并对密码进行加密处理:
import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import cn.mrzhqiang.security.crypto.password.PasswordEncoder;
import com.github.mrzhqiang.dagger2_example.account.Account;
import com.github.mrzhqiang.dagger2_example.di.DaggerActivityComponent;
import javax.inject.Inject;
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.username,
account.password,
passwordEncoder.encode(account.password));
contentText.setText(content);
}
}
3.3 运行
现在运行一下,看看效果:
看到了吧,即使是 123456
这样的密码,经过加密处理后,也是普通暴力破解望而却步的长度和复杂度。
当然,更说明依赖注入现在是正常生效,并且 @Provides
更符合日常开发使用。
3.4 总结
@Provides
把第三方库依赖接入 Dagger2
的管理中,并带来一个全新的概念——@Module
模块化。
我们可以设计 NetworkModule
、DatabaseModule
、SecurityModule
等等模块,它们可以完全独立,也可以彼此关联。
一旦模块分类完毕,后续的依赖管理就变得轻松简单。
下一章,我们要实现组件和依赖的单例模式,以及如何巧妙避免注入时的重复代码。
网友评论