module解耦(四)基于依赖注入的解耦

安卓开发中基于依赖注入(DI)的模块解耦

1. 什么是依赖注入(DI)

依赖注入(Dependency Injection,简称 DI)是一种设计模式,用于实现控制反转(Inversion of Control,简称 IoC)。控制反转是指将对象之间的依赖关系由程序代码中定义转移到外部容器中管理,从而降低对象之间的耦合度,提高代码的可维护性和可测试性

依赖注入的核心思想是:一个类不需要自己创建或获取它所依赖的其他类的实例,而是通过构造函数、属性或者工厂模式等方法,由外部容器(如 DI 框架)将它所依赖的其他类的实例注入到它内部

例如,假设有一个名为 Car 的类,它需要一个名为 Engine 的类作为它的属性。如果不使用依赖注入,Car 类可能会在自己的构造函数中创建或获取一个 Engine 类的实例:

public class Car {
    private Engine engine;

    public Car() {
        // 不使用依赖注入
        this.engine = new Engine(); // 或者从其他地方获取
    }
}

这样做会导致 Car 类和 Engine 类之间产生强耦合关系,如果 Engine 类发生变化或者需要替换成其他类型的引擎,Car 类也需要相应地修改代码。而且这样做也不利于对 Car 类进行单元测试,因为无法轻易地将 Engine 类替换成测试替身

如果使用依赖注入,则可以将 Engine 类作为 Car 类构造函数的参数传入:

public class Car {
    private Engine engine;

    public Car(Engine engine) {
        // 使用依赖注入
        this.engine = engine; // 由外部容器提供
    }
}

这样做就可以解除 Car 类和 Engine 类之间的强耦合关系,Car 类不需要关心 Engine 类如何创建或获取,只需要使用它提供的功能即可。同时也可以方便地对 Car 类进行单元测试,只需要传入一个名为 FakeEngine 的 Engine 测试替身,并针对不同的测试进行配置

2. 如何在安卓开发中使用 DI

在安卓开发中,有许多场景可以使用 DI 来实现模块解耦。例如,在企业级 APP 中, 路由是一种常见的功能, 用于将 Intent 页面跳转的强依赖关系解耦, 同时减少跨团队开发时互相依赖问题。 又例如,在大型 APP 中, 通常会采用模块化或组件化方式开发, 对于模块间解耦要求更高。 还有例如,在 MVP 或 MVVM 等架构模式中, 可以使用 DI 来管理 View 和 Presenter 或 ViewModel 之间以及各种服务和数据源之间的依赖关系

要在安卓开发中使用 DI ,我们可以借助一些开源库来简化操作和提高效率。 目前比较流行和成熟的 DI 开源库有 Dagger2 和 Dagger Hilt 等

安卓开发中基于依赖注入(DI)的模块解

1. 什么是依赖注入(DI)

依赖注入(Dependency Injection,简称 DI)是一种设计模式,用于实现控制反转(Inversion of Control,简称 IoC)。控制反转是指将对象之间的依赖关系由程序代码中定义转移到外部容器中管理,从而降低对象之间的耦合度,提高代码的可维护性和可测试性 。
依赖注入的核心思想是:一个类不需要自己创建或获取它所依赖的其他类的实例,而是通过构造函数、属性或者工厂模式等方法,由外部容器(如 DI 框架)将它所依赖的其他类的实例注入到它内部。
例如,假设有一个名为 Car 的类,它需要一个名为 Engine 的类作为它的属性。如果不使用依赖注入,Car 类可能会在自己的构造函数中创建或获取一个 Engine 类的实例:

 public class Car { 
private Engine engine; public Car() { 
// 不使用依赖注入 this.engine = new Engine(); 
// 或者从其他地方获取 
} 
}

这样做会导致 Car 类和 Engine 类之间产生强耦合关系,如果 Engine 类发生变化或者需要替换成其他类型的引擎,Car 类也需要相应地修改代码。而且这样做也不利于对 Car 类进行单元测试,因为无法轻易地将 Engine 类替换成测试替身。
如果使用依赖注入,则可以将 Engine 类作为 Car 类构造函数的参数传入:

public class Car {
private Engine engine;
public Car(Engine engine) { // 使用依赖注入
this.engine = engine; // 由外部容器提供
} 
} 

这样做就可以解除 Car 类和 Engine 类之间的强耦合关系,Car 类不需要关心 Engine 类如何创建或获取,只需要使用它提供的功能即可。同时也可以方便地对 Car 类进行单元测试,只需要传入一个名为 FakeEngine 的 Engine 测试替身,并针对不同的测试进行配置。

2. 如何在安卓开发中使用 DI

在安卓开发中,有许多场景可以使用 DI 来实现模块解耦。例如,在企业级 APP 中, 路由是一种常见的功能, 用于将 Intent 页面跳转的强依赖关系解耦, 同时减少跨团队开发时互相依赖问题。 又例如,在大型 APP 中, 通常会采用模块化或组件化方式开发, 对于模块间解耦要求更高。 还有例如,在 MVP 或 MVVM 等架构模式中, 可以使用 DI 来管理 View 和 Presenter 或 ViewModel 之间以及各种服务和数据源之间的依赖关系 。 要在安卓开发中使用 DI ,我们可以借助一些开源库来简化操作和提高效率。 目前比较流行和成熟的 DI 开源库有 Dagger2 和 Dagger Hilt 等

2.1 Dagger2

Dagger2 是一个由 Google 和 Square 共同开发的 DI 开源库,它基于 Java 注解和编译时代码生成技术,可以在编译期间检查依赖关系的完整性和正确性,从而提高运行时的性能和稳定性

要使用 Dagger2 ,我们需要遵循以下几个步骤:

① 定义依赖提供者(Provider),即用 @Provides 注解标注一个方法,表示该方法可以提供某个类型的依赖实例。例如:

@Module
public class EngineModule {
    @Provides
    public Engine provideEngine() {
        return new Engine();
    }
}

② 定义依赖需求者(Consumer),即用 @Inject 注解标注一个构造函数、属性或方法,表示该构造函数、属性或方法需要注入某个类型的依赖实例。例如:

public class Car {
    @Inject
    Engine engine;

    public Car() {
        // 省略其他代码
    }
}

③ 定义组件(Component),即用 @Component 注解标注一个接口,表示该接口可以连接依赖提供者和依赖需求者,并定义一些抽象方法来获取或注入依赖实例。例如:

@Component(modules = {EngineModule.class})
public interface CarComponent {
    // 获取依赖实例
    Engine getEngine();

    // 注入依赖实例
    void inject(Car car);
}

④ 生成并使用组件实例,即通过 Dagger 开头的类来获取组件实例,并调用其抽象方法来获取或注入依赖实例。例如:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 生成组件实例
        CarComponent carComponent = DaggerCarComponent.create();

        // 获取依赖实例
        Engine engine = carComponent.getEngine();

        // 注入依赖实例
        Car car = new Car();
        carComponent.inject(car);

        // 省略其他代码

    }
}

Dagger2 的优点有:

  • 编译时检查,避免运行时错误。
  • 无反射,提高性能。
  • 支持作用域管理,避免内存泄漏。
  • 支持多模块开发

Dagger2 的缺点有:

  • 学习曲线陡峭,注解繁多。
  • 需要手动创建和管理组件。
  • 需要适配 Android 生命周期

Dagger2 的使用场景有:

  • 需要高性能和稳定性的项目。
  • 需要灵活控制依赖关系的项目。
  • 需要支持多模块开发的项目

2.2 Dagger Hilt

Dagger Hilt 是一个由 Google 开发的 DI 开源库, 它是对 Dagger2 的二次封装, 专门针对 Android 平台制定了一系列规则, 大大简化了 Dagger2 的使用

要使用 Dagger Hilt ,我们需要遵循以下几个步骤:

① 定义依赖提供者(Provider),即用 @Provides 注解标注一个方法,表示该方法可以提供某个类型的依赖实例。与 Dagger2 不同的是,我们不需要自己创建模块类,而是用 @InstallIn 注解标注该方法所属的组件范围。例如:

@InstallIn(SingletonComponent.class)
@Module
public class EngineModule {
    @Provides
    public Engine provideEngine() {
        return new Engine();
    }
}

② 定义依赖需求者(Consumer),即用 @Inject 注解标注一个构造函数、属性或方法,表示该构造函数、属性或方法需要注入某个类型的依赖实例。与 Dagger2 不同的是,我们不需要自己创建和使用组件接口,而是用 @AndroidEntryPoint 注解标注 Android 系统组件(如 Activity、Fragment 等),表示该组件可以使用 Hilt 的依赖注入功能。例如:

@AndroidEntryPoint
public class MainActivity extends AppCompatActivity {

    @Inject
    Engine engine;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 省略其他代码

    }
}

③ 生成并使用组件实例,这一步由 Hilt 自动完成,我们不需要手动调用任何方法来获取或注入依赖实例。

Dagger Hilt 的优点有:

  • 简单易用,减少了大量的模板代码。
  • 提供了 Android 专属的 API ,适配了 Android 生命周期。
  • 支持作用域管理,避免内存泄漏。
  • 兼容 Dagger2 ,可以无缝迁移 。

Dagger Hilt 的缺点有:

  • 仍然基于 Dagger2 ,可能存在一些潜在的问题和限制。
  • 相对于 Dagger2 ,灵活性较低,不能自定义组件和模块 。

Dagger Hilt 的使用场景有:

  • 需要快速上手和简化开发流程的项目。
  • 需要适配 Android 生命周期和系统组件的项目。
  • 需要兼容 Dagger2 的项目 。
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容