在日常的开发中,通常我们在创建对象的时候,通过new关键字创建,这种创建方式并不优雅,通常来说,端上要拿到一个对象之后,是直接拿到用的,而不是说我需要自给自销,针对这种状况,依赖注入技术应运而生。
在Android中常用的注入框架有两种:Dagger2和Hilt,Hilt是JetPack组件中提供的依赖注入组件,是在Dagger2组件的基础上扩展,因此本章主要介绍Dagger2的使用及原理。
1 Dagger2的使用
如果要使用Dagger2,首先需要导入依赖
implementation 'com.google.dagger:dagger:2.6'
kapt 'com.google.dagger:dagger-compiler:2.6' //如果使用kotlin就需要使用kapt
看到annotationProcessor,如果之前看到编译时技术的文章就能知道,Dagger2一定是在编译期生成了一些类,运行的时候就可以直接调用。
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
}
既然用到了编译时技术,那么就需要引入Android的apt插件
1.1 从Dagger2注解了解依赖注入思想
前面我们讲到了,因为我们在使用某个类对象时,不需要自己再手动去创建,而是直接拿到用,那么就需要一个对象提供者。
@Module
public class BusinessModule {
@Provides
public static BusinessMode providerBusinessMode() {
return new BusinessMode();
}
}
例如BusinessModule是用于存储业务层需要的类对象,就可以使用@Module注解注释;
如果我们需要一个BusinessMode对象,那么提供providerBusinessMode方法,采用@Provides注解注释,通过providerBusinessMode就可以得到我们想要的这个类
在创建了Module之后,需要Component组件将这个依赖注入到某个Activity或者Fragment中
@Component(modules = {BusinessModule.class})
public interface BusinessComponent {
void inject(MainActivity mainActivity);
}
注意这里不能使用多态,需要明确到底要注入到哪一个Activity或者Fragment中。
@Inject
var businessMode:BusinessMode? = null
这样,通过Inject注解修饰我们想要的类,通过BusinessComponent具体实现类DaggerBusinessComponent实现注入
DaggerBusinessComponent.create().inject(this)
1.2 Dagger2使用中遇到的错误总结
错误1:
Dagger does not support injection into private fields
这个错误是Dagger不支持通过@Inject注解生成私有成员变量,我们通过编译后的代码可以看到,之前声明的businessMode对象,是私有的
@javax.inject.Inject()
private com.lay.image_process.dagger2.module.BusinessMode businessMode;
所以通过@JvmField注解修饰便可以解决这个问题
@Inject
@JvmField
var businessMode:BusinessMode? = null
错误2:
dagger.Provides missing element type
出现这个错误的原因就是dagger的版本和注解处理器的版本不一致导致的,即便是dagger有更新,只要compiler没有更新就不行,需要保持一致。
错误3:
java.lang.NoClassDefFoundError: javax/annotation/Generated
这是在编译时报错,需要我们添加javax annotation的依赖
implementation 'javax.annotation:javax.annotation-api:1.3.2'
kapt "javax.annotation:javax.annotation-api:1.3.2"
2 Dagger2的原理分析
2.1 Component组件实现注入原理
DaggerBusinessComponent.create().inject(this)
通过这一行代码,我们来看下,依赖注入到底是如何完成的。
首先我们先看下create方法做了什么事
public static Builder builder() {
return new Builder();
}
public static BusinessComponent create() {
return builder().build();
}
create方法的主要作用就是创建了Builder对象,并调用了build方法,看下Builder类。
public static final class Builder {
private Builder() {}
public BusinessComponent build() {
return new DaggerBusinessComponent(this);
}
/**
* @deprecated This module is declared, but an instance is not used in the component. This method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
*/
@Deprecated
public Builder businessModule(BusinessModule businessModule) {
Preconditions.checkNotNull(businessModule);
return this;
}
}
我们可以看到build方法就是将DaggerBusinessComponent创建出来,并将Builder作为参数传递进去,看下DaggerBusinessComponent的构造方法
private DaggerBusinessComponent(Builder builder) {
assert builder != null;
initialize(builder);
}
在DaggerBusinessComponent的构造方法中,调用了initialize初始化方法
private void initialize(final Builder builder) {
this.mainActivityMembersInjector =
MainActivity_MembersInjector.create(BusinessModule_ProviderBusinessModeFactory.create());
}
在这个方法中,主要就是将MainActivity_MembersInjector类初始化,调用了这个类的create方法
public static MembersInjector<MainActivity> create(Provider<BusinessMode> businessModeProvider) {
return new MainActivity_MembersInjector(businessModeProvider);
}
在MainActivity_MembersInjector的create方法中,同样也是new出来一个对象,businessModeProvider就是我们需要注入的某个类对象的提供者,对应@Provides注解这个方法。
所以整个create方法做了2件事:
(1)创建具体的DaggerBusinessComponent对象;
(2)创建具体的注入类对象,并初始化Module提供者
然后调用了inject方法
@Override
public void inject(MainActivity mainActivity) {
mainActivityMembersInjector.injectMembers(mainActivity);
}
在这个方法中,调用了mainActivityMembersInjector的injectMembers方法
@Override
public void injectMembers(MainActivity instance) {
if (instance == null) {
throw new NullPointerException("Cannot inject members into a null reference");
}
instance.businessMode = businessModeProvider.get();
}
在这个方法中,就是将MainActivity中的businessMode变量(这里就是为啥不能声明为私有变量的原因,没法调用)赋值,businessModeProvider其实就是数据提供者,调用的get方法就是调用BusinessModule中的providerBusinessMode方法
@Override
public BusinessMode get() {
return Preconditions.checkNotNull(
BusinessModule.providerBusinessMode(),
"Cannot return null from a non-@Nullable @Provides method");
}
也就是说
instance.businessMode = businessModeProvider.get();
等价于
instance.businessMode = new BusinessMode();
所以我们可以这样理解:@Component是@Inject和@Module建立沟通的桥梁
2.2 多Component注入
在之前,我们是将BusinessComponent注入到了MainActivity中,假设我们再写一个组件,也注入MainActivity中,这样可以吗?
@Module
class CarModule {
@Provides
fun provideCar(): Car {
return Car()
}
}
@Component(modules = [CarModule::class])
interface CarComponent {
fun inject(activity: MainActivity)
}
错误: com.lay.image_process.dagger2.module.BusinessMode cannot be provided without an @Inject constructor or from an @Provides- or @Produces-annotated method.
public abstract void inject(@org.jetbrains.annotations.NotNull()
我们编译工程之后,发现已经报错了,也就是说,同一个Activity或者Fragment只能注入一个Component
那如果我们想要注入多个Component该怎么做呢?
@Component(modules = [CarModule::class])
interface CarComponent {
fun getCar():Car
}
我们看到,在Component注解中,除了modules之外,还可以配置dependencies
Class<?>[] modules() default {};
/**
* A list of types that are to be used as <a href="#component-dependencies">component
* dependencies</a>.
*/
Class<?>[] dependencies() default {};
通过dependencies将CarComponent引进来,就相当于将两个组件组合起来一起给MainActivity使用
@Component(modules = {BusinessModule.class}, dependencies = {CarComponent.class})
public interface BusinessComponent {
void inject(MainActivity mainActivity);
}
那么在使用的时候就不能直接使用create创建DaggerBusinessComponent,这个时候会多出一个carComponent接口
DaggerBusinessComponent.builder()
.carComponent(DaggerCarComponent.create())
.build().inject(this)
还有一种方式,@SubComponent注解,但是这种方式不是很灵活
2.3 类之间相互依赖
@Module
class CarModule {
@Provides
fun provideCar(): Car {
return Car()
}
@Provides
fun providerCarFactory(car: Car): CarFactory {
return CarFactory(car)
}
}
像CarFactory类中需要传入Car作为参数,那么只需要同样声明一个提供Car对象的方法,这样就能直接注入到CarFactory的构造方法中。
@Override
public CarFactory get() {
return Preconditions.checkNotNull(
module.providerCarFactory(carProvider.get()),
"Cannot return null from a non-@Nullable @Provides method");
}
通过源码我们可以看到就是,通过carProvider获取到car实例,然后传入了providerCarFactory方法中
对于Dagger2来说,我们只需要掌握的就是它的注入思想,但是真正在项目中使用的时候,还是得Hilt出马