什么是依赖注入
类可通过以下三种方式获取所需的对象:
- 类构造其所需的依赖项。类创建并初始化自己的依赖类型的实例。
- 从其他地方抓取。某些 Android API(如Contextgetter 和 getSystemService())的工作原理便是如此。
- 以参数形式提供。应用可以在构造类时提供这些依赖项,或者将这些依赖项传入需要各个依赖项的函数。
第三种方式就是依赖项注入。
依赖注入的优点
总的来说:易于复用,易于重构,方便测试
重用类以及分离依赖项:更容易换掉依赖项的实现。由于控制反转,代码重用得以改进,并且类不再控制其依赖项的创建方式,而是支持任何配置。比如Car依赖Engine,那么将Engine作为依赖注入,而不是Car中创建Engine实例。如果有Engine的子类想要用Car,则可以直接使用。
易于重构:依赖项成为 API Surface 的可验证部分,因此可以在创建对象时或编译时进行检查,而不是作为实现详情隐藏。
易于测试:类不管理其依赖项,因此在测试时,可以传入不同的实现以测试所有不同用例。
Android 中有两种主要的依赖项注入方式
- 构造函数注入
- 字段注入(setter注入)
这两种都是手动依赖项的注入。有如下缺陷:
自动注入依赖项
一,两种思路
- 基于反射的解决思路,在运行时连接依赖项
- 静态解决方案,可生成在编译时连接依赖项的代码
二,服务定位器 ServiceLocator
使用服务定位器模式,类可以控制并请求注入对象;使用依赖项注入,应用可以控制并主动注入所需对象。
class ServiceLocator {
private static ServiceLocator instance = null;
private ServiceLocator() {}
public static ServiceLocator getInstance() {
if (instance == null) {
synchronized(ServiceLocator.class) {
instance = new ServiceLocator();
}
}
return instance;
}
public Engine getEngine() {
return new Engine();
}
}
class Car {
private Engine engine = ServiceLocator.getInstance().getEngine();
public void start() {
engine.start();
}
}
class MyApp {
public static void main(String[] args) {
Car car = new Car();
car.start();
}
}
服务定位器其实会有以下问题:
-
服务定位器所需的依赖项集合使得代码更难测试,因为所有测试都必须与同一全局服务定位器进行交互。
-
依赖项在类实现中编码,而不是在 API Surface 中编码。因此,很难从外部了解类需要什么。所以,更改 Car或服务定位器中可用的依赖项可能会导致引用失败,从而导致运行时或测试失败。
-
如果您想将作用域限定为除了整个应用的生命周期之外的任何区间,就会更难管理对象的生命周期。
三,Dagger
四,Hilt
参考
https://developer.android.google.cn/training/dependency-injection#java
https://dagger.dev/