Android学习进阶——Dagger 2 使用及原理

news2024/9/27 12:07:56

概述

Dagger 2 是 Google 开源的一款依赖注入框架,它的前身是 square 的 Dagger 1,Dagger 2 在 Android 中有着较为广泛的应用。

Dagger 2 基于 Java 注解,采用 annotationProcessor(注解处理器) 在项目编译时动态生成依赖注入需要的 Java 代码,然后我们在合适的位置手动完成最终的依赖注入,而不是 Dagger 1 中基于反射的解决方案,所以在性能上是有保障的。

Dagger2的使用

首先我先用张图来简单说明一下Dagger2使用的流程

图中的Module是应用中常用的功能模块,比如说网络访问,数据存储等。这个类会有一个@Module的注解,具体的代码我会在后面详细介绍。其实这个Module的作用是提供各种功能对象,而这些Module会放到一个有@Component的容器类中,也就是图中Component类。当我们想使用各种功能对象进行业务操作的时候,只需要这个容器就能得到被注册了的功能对象,图中的意思是在Activity中使用对象进行业务操作,当然也不仅限于Activity。

上图相对来说还是太简略了,并没有完整的表达出Dagger2的原理,下面我们直接从代码中感受一个Dagger2的强大。

1.引入Dagger2

implementation 'com.google.dagger:dagger:2.4'
annotationProcessor 'com.google.dagger:dagger-compiler:2.4'

2.创建网络访问对象及其Module模块

首先创建一个HttpObject,我们假设这个HttpObject中有各种网络的操作,get,post,put等

public void get(){
        Log.i("Dagger2","这里是get方法");
    }
    
    public void post(){
        Log.i("Dagger2","这里是post方法");
    }
}
创建HttpModule
​
@Module
public class HttpModule {
    @Provides
    public HttpObject providerHttpObject(){
        return new HttpObject();
    }
}

HttpModule的两个注解是需要注意的地方:@Module这个注解相当于给当前类打了一个标记,表明了这个类的类型,便于注入到容器中;@Provides这个注解放在了方法的上面,从上面的代码可以看出来,主要就是创建功能对象。

3.创建容器Component

@Component(modules = {HttpModule.class})
public interface MyComponent {
    void injectMainActivity(MainActivity mainActivity);
}

容器这个类被@Component所注解,而且是一个接口类,@Component中的modules参数接收的类型是一个数组,表示被装入容器的Module有哪些。injectMainActivity方法表示这个容器中的功能对象(例如HttpObject)会在哪个类使用,我这里使用的MainActivity做的测试,所以参数写的是MainActivity。

4.在类中使用HttpObject

在配置完上述的代码之后,一定先rebuild!

public class MainActivity extends AppCompatActivity {
    @Inject
    HttpObject mHttpObject;
​
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DaggerMyComponent.create().injectMainActivity(this);
        mHttpObject.get();
        mHttpObject.post();
    }
}

因为我们要使用的类是HttpObject,所以在MainActivity创建这个类的对象,然后被@Inject所注解。要注意的是DaggerMyComponent这个类是rebuild之后生成的,调用DaggerMyComponent.create().inkectMainActivity(this)这句话来生成mHttpObject对象,调用HttpObject中的get和post方法就会有相应的输出。

Dagger 2原理

相比机械性的记住这些注解以及使用流程,Dagger 2 背后的工作原理更加重要,这样我们将明白各个模块的职责,以及框架是如何将它们关联起来帮助我们完成依赖注入的,明白了原理后使用 Dagger 2 就会更加得心应手了。

所以我们这里先了解 Dagger 2 背后的基本原理,然后再学习其它内容。

以前边的demo为例,项目编译后会在app\build\generated\source\apt\debug\包名目录下生成依赖注入的相关类,如下:

按照之前说法,DaggerMainComponent是完成依赖注入的核心,所以从这个类开始分析,它的源码如下:

public final class DaggerMainComponent implements MainComponent {
  private MainModule mainModule;
​
  private DaggerMainComponent(Builder builder) {
    initialize(builder);
  }
​
  public static Builder builder() {
    return new Builder();
  }
​
  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    this.mainModule = builder.mainModule;
  }
  // 重写inject方法,完成依赖注入
  @Override
  public void inject(MainActivity activity) {
    injectMainActivity(activity);
  }
  // 完成依赖注入的重要方法
  private MainActivity injectMainActivity(MainActivity instance) {
    // 给 MainActivity 中的 cat 成员变量赋值
    MainActivity_MembersInjector.injectCat(instance, new Cat());
    // 给 MainActivity 中的 flower 成员变量赋值
    MainActivity_MembersInjector.injectFlower1(
        instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
    return instance;
  }
 
  public static final class Builder {
    private MainModule mainModule;
​
    private Builder() {}
    // 完成DaggerMainComponent对象的创建
    public MainComponent build() {
      if (mainModule == null) {
        this.mainModule = new MainModule();
      }
      return new DaggerMainComponent(this);
    }
    // 设置 mainModule 对象
    public Builder mainModule(MainModule mainModule) {
      this.mainModule = Preconditions.checkNotNull(mainModule);
      return this;
    }
  }
}

典型的 Builder 构建模式,再结合之前 DaggerMainComponent 的用法来分析:

DaggerMainComponent.builder()
                .mainModule(new MainModule())
                .build()
                .inject(this);

先通过builder()方法创建一个 Builder 对象,再通过其mainModule()方法设置mainModule 对象,接下来用 build()方法就是创建DaggerMainComponent 对象,这样它里边也有了一个mainModule对象。

在 DaggerMainComponent 中还重写了 MainComponent 接口的inject()方法,里边调用的injectMainActivity()方法是完成依赖注入的关键:

private MainActivity injectMainActivity(MainActivity instance) {
    MainActivity_MembersInjector.injectCat(instance, new Cat());
    MainActivity_MembersInjector.injectFlower1(
        instance, MainModule_ProvideRedRoseFactory.proxyProvideRedRose(mainModule));
    return instance;
  }

首先是调用MainActivity_MembersInjector类的injectCat()方法直接创建一个 Cat 对象,完成 MainActivity 中 cat 的赋值,injectCat()方法声明如下:

public static void injectCat(MainActivity instance, Cat cat) {
    instance.cat = cat;
  }

然后是调用injectFlower()方法,完成 MainActivity 中 flower 的赋值,那么 flower 对象的值从哪里来呢?这里调用了MainModule_ProvideRedRoseFactory的proxyProvideRedRose()方法:

public static Flower proxyProvideRedRose(MainModule instance) {
    return Preconditions.checkNotNull(
        instance.provideRedRose(), "Cannot return null from a non-@Nullable @Provides method");
  }

里边最终是调用了我们在 MainModule 中声明的 provideRedRose() 方法,所以在 DaggerMainComponent 内部是通过 MainActivity_MembersInjector 完成了最终的依赖注入。所以当在 Activity 中执行inject(this)方法时,就是开始创建依赖对象,并完成注入工作。

到这里整个依赖注入的流程就结束了,从源码的角度来看,整个过程更像是医生给患者注射药物。我们可以把依赖注入组件 DaggerMainComponent 看做“医生”,把 MainActivity_MembersInjector 看做“注射器”,MainModule 就是“药物”,MainActivity 就是“患者”,医生用注射器把药物送到患者体内。

Dagger的优点

有的小伙伴会问,你整这么一大堆是为了啥?要不然直接创建对象,要不然创建一个单例,多省事,可比Dagger2的这种方式方便多了。在中大型项目中,类似于HttpObject这种对象会被大量的应用,如果突然有一天这个类的初始化方法改变了,你岂不是要修改每一处吗。即便是使用单例getInstance方法也避免不了这种问题,因为如果在创建对象的时候需要在构造器中添加一个参数,每一处的getInstance也需要被修改。而Dagger2完美的避免了这种问题。

以上就是Android中Dagger 2的基本使用以及原理的学习,有关Android开发中还有更多的学习技术;这里推荐参考《Android核心技术手册》这个大文档里面记录了30多个技术板块。几千个小知识点带你引进更高的技术层面。

总结

Dagger 2 的主要内容就这些了,使用 Dagger 2 必然要编写相关的辅助类、接口、使用各种注解,虽然没有直接 new 一个对象或者传统的依赖注入方式简单,但 Dagger 2 带来的是更好的代码解耦,更有利于后期的扩展维护,对于那些需要长期维护的项目这一点是更加重要的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/129664.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AS-REPRoasting

讲在前面: 最近笔者发布的几篇文章,大家不难发现,都是和kerberos协议相关的。国内外各类的研究员对于该协议的研究愈发的深入,从该协议产生的漏洞就会越来越多甚至越来越严重。当然这只是笔者的一点拙见。 在介绍了Kerberoastin…

21天Java开发速成篇-Java从入门到大师01快速入门

Java 简介 win10安装 JDK 下载和安装 访问 oracle 官网https://www.oracle.com/ 建议下载解压版,下载完毕后,解压到一个没有中文和空格的目录即可 配置 Path 你可以选择一个喜欢的目录,我的解压目录是 d 盘根目录下,如图所示 其中 b…

创邻Galaxybase入选 2022 爱分析《中国数据智能最佳实践案例》

随着2022年进入收官时间,创邻科技喜报频传。近日,“2022爱分析中国数据智能最佳实践案例”评选结果出炉,经过申报、初评、调研、终评等多轮角逐,创邻科技凭借“中国民生银行万象知识图谱应用平台”成功获评2022中国数据智能最佳实…

FPGA知识汇集-FPGA时序基础理论

对于系统设计工程师来说,时序问题在设计中是至关重要的,尤其是随着时钟频率的提高,留给数据传输的有效读写窗口越来越小,要想在很短的时间限制里,让数据信号从驱动端完整地传送到接收端,就必须进行精确的时…

Kubernetes:minikube操作总结

Kubernetes:minikube操作总结minikube statusminikube nodeminikube status 查询集群状态 $ minikube statusminikube node 新增节点 注意:minikube 仅仅是基于本地 standalone 部署一款简易 k8s 集群,因此添加节点,也仅仅是本…

【多尺度条形池化与通道注意力的图像语义分割】论文精讲

论文:论文链接 出处:中国图象图形学报 目录 1.摘要 2.网络结构 2. 1 改进的条形池化技术 2.2 二阶通道注意力机制 3.结果 1.摘要 目的 针对自然场景下图像语义分割易受物体自身形状多样性、距离和光照等因素影响的问题,本文提出 一种新的基于条形池…

windows搭建gitblit服务器

使用Gitblit搭建属于局域网的Git服务器,方便程序代码的联合开发及程序代码版本管理。 环境:1、Windows 64位操作系统。 2、jdk1.8.0(需要java环境,已安装请忽略) 3、Git-2.24.1.2-64-bit(git工具&#x…

linux系统编程:暂停函数pause

pause函数 该函数功能主要是暂停进程,它的返回值总是-1。 使用方式: (1)首先使用signal函数提前注册一个中断函数,该函数用于将函数指针和信号做一个绑定; (2)当程序进行执行pause&#xff0…

【Bp2Lua】深入理解蓝图

【Bp2Lua】深入理解蓝图 做 Bp2Lua 过程中的副产物,蓝图在连连看背后隐含的操作 有几点想说明的 蓝图会隐含一些操作,前端节点图是看不出来的,但是后端字节码会一览无余Bp2Lua 本质是 字节码 ToLua,其实 Bp2Lua 根本不关心也不…

策略模式(strategy pattern)

背景 一、一个具体实现范例的逐步重构 补血道具(药品):a) 补血丹:补充200点生命值;b) 大还丹:补充300点生命值;c) 守护丹:补充500点生命值将Fighter,F_Warrior,F_Mage 单独写在一个文件中。 Fighter.h …

一步一步学爬虫(4)数据存储之文本存储

一步一步学爬虫(4)数据存储之文本存储4.1 TXT纯文本文件存储4.1.1 本节目标4.1.2 基本实例4.1.3 打开方式4.1.4 简化写法4.1 TXT纯文本文件存储 将数据保存到 TXT 文本的操作非常简单,而且 TXT 文本几乎兼容任何平台,但是这有个缺…

Spark环境搭建(Hadoop YARN模式)

前言 按照前面环境部署中所学习的,如果我们想要一个稳定的生产Spark环境,那么最优的选择就是构建:HA StandAlone集 群。 不过在企业中, 服务器的资源总是紧张的,许多企业不管做什么业务,都基本上会有Hadoo…

AD软件绘制不规则焊盘的器件封装

网上有很多关于AD软件绘制不规则焊盘的帖子,搜了一些帖子看了一下,感觉不太对。严格意义上AD软件是不能绘制不规则的焊盘的,至少目前用的AD软件不支持。为什么这么说呢? 我提一个需求:假如我在PCB文件中需要随意的添加…

企业选择SOP作业指导书系统的目的和意义

SOP是将作业指导流程予以说明规范,让作业人员有一个标准的作业准则,以达到作业的标准一致性。也是一种管理模式,通过对过程的标准化操作,减少和预防差错和不良后果的发生。通过动作的解析、比较、分析,循环作业分析等输…

MIT6.830-2022-lab4实验思路详细讲解

目录前言一、实验概览Exercise 1:Granting LocksExercise 2:Lock LifetimeExercise 3:Implementing NO STEALExercise 4:TransactionsExercise 5:Deadlocks and Aborts总结 && Debug记录前言 到lab4事务,这一块应该是数据库中概念比较多&#x…

2022/12/30总结

今日学习了二叉树有关知识。 二叉树 二叉树通俗来讲就是一个有俩个指针的链表。他们大多长这个样子: 这里还有俩个概念了,二叉树分为完全二叉树和满二叉树 上面所说的是满二叉树,顾名思义就是每个父节点都相应的有俩个指针,通常…

基于springboot+Vue的宿舍管理系统前后端分离(程序+详细文档+数据库)

大家好✌!我是CZ淡陌。一名专注以理论为基础实战为主的技术博主,将再这里为大家分享优质的实战项目,本人在Java毕业设计领域有多年的经验,陆续会更新更多优质的Java实战项目,希望你能有所收获,少走一些弯路…

前端devops——利用gitlab实现CI/CD自动化部署

目录 前言 一、前期准备 1、开启虚拟服务 2、下载并安装docker 二、开始部署 1、安装gitlab 2、修改默认账号登录密码 3、修改项目clone地址 三、Gitlab CI/CD 1、安装并运行gitlab-runner 2、执行runner 3、将项目注册到gitlab-runner 1、获取token 2、执行注册 …

ETL数据清洗

大多数据仓库的数据架构可以概括为: 数据源-->ODS(操作型数据存储)-->DW-->DM(data mart) ETL贯穿其各个环节。 ​一、数据抽取: 可以理解为是把源数据的数据抽取到ODS或者DW中。 1. 源数据类型: 关系型数据库,如Or…

多模态串讲(上)

多模态的学习在最近几年异常火爆,除了普通的多模态学习,比如视觉问答,图文检索等,其实之前讲的所有这种Language Guided Detection,或者Language Guided Segmentation,这些任务都是多模态的,还有最近火的文…