什么是依赖项注入
依赖项注入(DI)是一种广泛用于编程的技术,是一种对象接收其依赖的其他对象的技术,这些其他对象称为依赖项。类通常需要引用其他类,类可以通过以下三种方式获取所需的对象:
1.直接在类构成其所需的依赖项。
2.从其他地方抽取。
3.以参数形式提供。应用可以在构造类时提供这些依赖项,或者将这些依赖传入需要各个依赖项的函数。
上面的三种方式中,第三种方式就是依赖项注入!使用这种方法,可以获取并提供类的依赖项,而不必让类实例自行获取。
在Android中有两种依赖项注入方法:
- 构造函数注入。将某个类的依赖项传入其构造函数。
- 字段注入(或setter注入)。
示例
以汽车和引擎为例,汽车需要依赖引擎才能发动,以下分别通过两种方式实现:非依赖注入和依赖注入。
非依赖注入的方式:
fun main(){
val car = Car()
car.start()
}
class Car(){
private val mEgine=Engine()
fun start(){
mEgine.start()
}
}
class Engine{
fun start(){
}
}
依赖注入的方式:
构造函数注入
fun main(){
val engine = Engine()
val car = Car(engine)
car.start()
}
class Car(val engine:Engine){
fun start(){
engine.start()
}
}
class Engine{
fun start(){
}
}
字段注入
fun main(){
val car = Car()
car.mEgine=Engine()
car.start()
}
class Car(){
lateinit var mEgine: Engine
fun start(){
mEgine.start()
}
}
class Engine{
fun start(){
}
}
自动依赖项注入
程序员自行创建,提供并管理不同类的依赖项,而不依赖于库的依赖注入方式,称为手动依赖项注入或人工依赖项注入。随着依赖项和类增多,手动依赖项注入就越繁琐。手动依赖项注入还会带来多个问题:
- 对于大型应用,获取所有依赖项并正确连接它们可能需要大量得样板代码。在多层架构中,要为顶层创建一个对象,必须提供其下层得所有依赖项。
- 如果无法在传入依赖项之前构造依赖项,则需要编写并维护管理内存中依赖项生命周期得自定义容器。
解决这类问题得方法可以归为两类:
- 基于反射得解决方案,可以在运行时连接依赖项代码。
- 静态解决方案,可在编译时生成连接依赖项得代码。
Dagger是适用于Android的热门依赖库注入库,它提供了完全静态和编译时依赖项,解决了基于反射方案的开发和性能问题。
依赖项注入的优势
- 重用类以及分离依赖项:更容易换掉依赖项的实现。由于控制反转,代码重用得以改进,并且类不再控制其他依赖项的创建方式,而是支持任何配置。
- 易于重构:依赖项成为API Surface的可验证部分,因此可再创建对象时或编译时进行检查,而不时作为实现详情隐藏。
- 易于测试:类不管理其依赖项,因此再测试时,你可以出传入不同的实现以测试所有不同用例。
Dagger在Android上的简单使用
添加依赖
implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'
Dagger的注解解析
- @Inject: 有两个作用:一个是在需要依赖的类(目标类,即宿主)中标记成员变量告诉Dagger这个类型的变量需要一个实例对象。二是标记类中的构造方法(一般为无参构造方法)告诉Dagger我可以提供这种类型的依赖实例。
- @Provides: 用来提供依赖实例,对方法进行注解,且都是有返回类型的。用来告诉Dagger,我们想如何创建并提供该类型的依赖实例(一般会在方法中new出实例)。用@Provides标记的方法,谷歌推荐采用provide是为前缀,必须用在@Module注解的类中,方法所需的参数也需要以方法的形式返回提供。
- @Module: 用来标记类(一般类名以Module结尾)。Module主要的作用是用来集中管理@Provides标记的方法。我们定义一个被@Module注解的类,Dagger就会知道在哪里找到依赖来满足创建类的实例。modules的一个重要特征是被设计成区块并可以组合在一起供@Component所注解的类使用。
- @Component:用来标记接口或者抽象类(一般以Component结尾),是@Inject(指第一个作用)和@Module之间的桥梁,主要职责是把二者组合在一起,Module中的实例对象必须在Component中暴露出来才能供之后使用。所有的components都可以通过它的modules知道它所提供的依赖范围。一个Component可以依赖一个或多个Component,并拿到被依赖Component暴露出来的实例,Component的dependencies属性就是确定依赖关系的实现。
- @Scope:作用域,Dagger2通过自定义注解来限定作用域,有一个默认的作用域注解@Singleton,通常在Android中用来标记在App整个生命周期内存活的实例。也可以自定义一个@PerActivity、@PerFragment注解,用来表明实例生命周期与Activity、Fragment一致。我们可以自定义作用域的粒度(比如@PerUser等等)。
- @Qualifier:限定符。当一个类的类型不足以标示一个依赖的时候,我们就可以用这个注解。
- @SubComponent:如果我们需要父组件全部的提供对象,这时我们可以用包含方式而不是用依赖方式,相比于依赖方式,包含方式不需要父组件显式显露对象(依赖方式只能拿到暴露出的实例),就可以拿到父组件全部对象。且SubComponent只需要在父Component接口中声明就可以了。
基本的使用
1.通过@Inject、@Component实现依赖注入。
- 定义一个需要依赖注入的对象
public class User {
private String name;
@Inject
public User(){
this.name="Dagger";
}
public String getName() {
return name;
}
}
- 定义一个Component
/**
* 定义一个桥梁Component
*/
@Component
public interface MainComponent {
/**
*
* 方法的参数传入需要注入的类名,不能是其父类。方法名可以自定义。
* @param mainActivity
*/
void inject(MainActivity mainActivity);
}
- 提供一个宿主
public class MainActivity extends AppCompatActivity {
//不能修饰private的变量
@Inject
public User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
点击Make Project就会自动生成相应的代码。然后就可以通过生成的以Dagger为前缀的类,完成依赖对象的注入。
DaggerMainComponent.builder().build().inject(this);
用@Inject存在些个缺点
- @Inject注解的构造方法有参数的话,参数也需要有其他地方提供依赖
- @Inject注解了一个对象的两个构造方法就会报错,dagger不知道用哪个进行实例化
- 对于第三方库或者一些我们无法修改构造方法的对象无法使用@Inject
2.通过@Provides,@Module配合提供实例
- User函数的修改
public class User {
private String name;
//这里不再需要Inject
public User(
String name
){
this.name=name;
}
public String getName() {
return name;
}
}
- 创建一个Module来管理@Provides注解的方法
@Module
public class MainModule {
@Provides
public User provideUsr(){
return new User("Dagger");
}
}
- Component的修改
/**
* 定义一个桥梁Component
*/
@Component(modules = {MainModule.class})
public interface MainComponent {
/**
*
* 方法的参数传入需要注入的类名,不能是其父类。方法名可以自定义。
* @param mainActivity
*/
void inject(MainActivity mainActivity);
}
Make Project的之后,就可以在目标类中初始化注入
public class MainActivity extends AppCompatActivity {
@Inject
public User user;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerMainComponent.create().inject(this);
}
}
以上是Dagger的简单使用,Dagger还有很多功能,需要后续去学习和完善。
网友评论