Android开源框架--Dagger2详解

功名只向马上取,真是英雄一丈夫

一,定义

我们知道在一个类中,通常会定义其他类型的变量,这个变量就是我们所说的“依赖“。

对一个类的变量进行初始化,有两种方式。第一种,这个类自己进行初始化;第二种,其他外部的类帮你进行初始化。

其中第二种方式,由外部类进行初始化的方式就是我们所说的”依赖注入“。由于他是由外部来控制,因此又叫做”控制反转“。依赖注入和非依赖注入的区别就是,变量初始化工作是由谁来做的。前面我们提到过的创建型设计模式,工厂模式、builder模式,带参数的构造函数等,都是依赖注入。

因此,实际上我们一直都在使用“依赖注入“,对它其实并不陌生。而Dagger2的定位就是提供了用注解的方式来配置依赖的方法。因此Dagger2并不是提供依赖注入的能力,而是为依赖注入提供一种更简单的方法。
谷歌新推出的hilt相比于dagger2来说,使用起来要方便易懂的多,那么我们为什么还要去学习dagger2呢?因为dagger2与hilt的关系,就相当于okhttp3与retrofit2的关系。hilt只是对于dagger2的二次封装。而且现在许多大型的互联网项目使用的依然是dagger2,所以我们非常有必要去了解dagger2。

二,角色介绍

1,object:需要被创建的对象

2,module: 主要用来提供对象

3,component:用于组织module并进行注入

三,基本使用

1,在app的build.gradle下面添加依赖:

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

如果报错:Unable to load class 'javax.annotation.Generated'.

再添加依赖:

implementation'javax.annotation:javax.annotation-api:1.3.2'annotationProcessor("javax.annotation:javax.annotation-api:1.3.2")

2,创建一个用于注入的对象:

public class YuanZhen {
}

3,创建一个module,用来提供YuanZhen

@Module
public class YuanZhenModule {@Providespublic YuanZhen providerYuanZhen(){//后面所有需要YuanZhen对象的地方,都在这里提供return new YuanZhen();}
}

4,创建component,用于组织module并进行注入

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

5,注入到activity

public class MainActivity extends AppCompatActivity {@InjectYuanZhen yuanZhen;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}
}

6,编译项目,让APT生成需要的文件

关于APT不了解的,可以参考文章android注解之APT和javapoet_android javapoet-CSDN博客

7,使用:

public class MainActivity extends AppCompatActivity {@InjectYuanZhen yuanZhen;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//  DaggerMyComponent.create().injectMainActivity(this);DaggerMyComponent.builder().yuanZhenModule(new YuanZhenModule()).build().injectMainActivity(this);System.out.println("yz----"+yuanZhen.hashCode());}}

最后输出:

上面就是APT的基本使用,两种方式都可以

四,源码分析

对于源码的分析,主要是分析APT生成的三个文件

从 DaggerMyComponent.builder().yuanZhenModule(new YuanZhenModule()).build().injectMainActivity(this);

我们可以看出,一定是采用了Buidler建造者模式,不清楚的可以参考文章Android设计模式--Builder建造者模式-CSDN博客

首先分析DaggerMyComponent.builder()

public static Builder builder() {return new Builder();
}

可以看到是new了一个Buidler,我们再来看下Builder

public static final class Builder {private YuanZhenModule yuanZhenModule;private Builder() {}public MyComponent build() {if (yuanZhenModule == null) {this.yuanZhenModule = new YuanZhenModule();}return new DaggerMyComponent(this);}public Builder yuanZhenModule(YuanZhenModule yuanZhenModule) {this.yuanZhenModule = Preconditions.checkNotNull(yuanZhenModule);return this;}

 可以看到DaggerMyComponent.builder().yuanZhenModule(new YuanZhenModule())是将我们新创建的YuanZhenModule传入到了Builder里面。

然后 DaggerMyComponent.builder().yuanZhenModule(new YuanZhenModule()).build()则是创建了一个DaggerMyComponent对象。

private DaggerMyComponent(Builder builder) {assert builder != null;initialize(builder);
}

在DaggerMyComponent的构造函数中,调用了initialize方法。

private void initialize(final Builder builder) {this.providerYuanZhenProvider =YuanZhenModule_ProviderYuanZhenFactory.create(builder.yuanZhenModule);this.mainActivityMembersInjector =MainActivity_MembersInjector.create(providerYuanZhenProvider);
}

在initialize方法中,调用了YuanZhenModule_ProviderYuanZhenFactory的create方法,这是一个工厂模式,不清楚的请参考Android设计模式--工厂模式-CSDN博客

再来看看YuanZhenModule_ProviderYuanZhenFactory的create方法

public static Factory<YuanZhen> create(YuanZhenModule module) {return new YuanZhenModule_ProviderYuanZhenFactory(module);
}

创建了一个YuanZhenModule_ProviderYuanZhenFactory对象,接下来继续看YuanZhenModule_ProviderYuanZhenFactory的构造方法:

public YuanZhenModule_ProviderYuanZhenFactory(YuanZhenModule module) {assert module != null;this.module = module;
}

也就是将yuanZhenModule这个对象传递给了YuanZhenModule_ProviderYuanZhenFactory

然后我们继续回到initialize方法,看看 this.mainActivityMembersInjector =
      MainActivity_MembersInjector.create(providerYuanZhenProvider)的实现

public static MembersInjector<MainActivity> create(Provider<YuanZhen> yuanZhenProvider) {return new MainActivity_MembersInjector(yuanZhenProvider);
}

创建了一个MainActivity_MembersInjector对象,看看其构造方法:

public MainActivity_MembersInjector(Provider<YuanZhen> yuanZhenProvider) {assert yuanZhenProvider != null;this.yuanZhenProvider = yuanZhenProvider;
}

将刚才创建的YuanZhenModule_ProviderYuanZhenFactory对象传递给了MainActivity_MembersInjector;

最后 我们来看下

DaggerMyComponent.builder().yuanZhenModule(new YuanZhenModule()).build().injectMainActivity(this);

@Override
public void injectMainActivity(MainActivity mainActivity) {mainActivityMembersInjector.injectMembers(mainActivity);
}

调用了MainActivity_MembersInjector的injectMembers方法

@Override
public void injectMembers(MainActivity instance) {if (instance == null) {throw new NullPointerException("Cannot inject members into a null reference");}instance.yuanZhen = yuanZhenProvider.get();
}

然后又调用了YuanZhenModule_ProviderYuanZhenFactory的get方法

@Override
public YuanZhen get() {return Preconditions.checkNotNull(module.providerYuanZhen(), "Cannot return null from a non-@Nullable @Provides method");
}

在get方法中,最终调用了我们自己写的module的providerYuanZhen方法:

@Module
public class YuanZhenModule {@Providespublic YuanZhen providerYuanZhen(){//后面所有需要YuanZhen对象的地方,都在这里提供return new YuanZhen();}
}

这样就通过APT生成的代码,自动帮我们创建了对象。

五,单例使用

1,创建一个注解:

@Scope
@Documented
@Retention(RUNTIME)
public @interface AppScope {}

2,在component和Module中使用注解

@AppScope
@Component(modules = {YuanZhenModule.class})
public interface MyComponent {void injectMainActivity(MainActivity mainActivity);
}
@AppScope
@Module
public class YuanZhenModule {@AppScope@Providespublic YuanZhen providerYuanZhen(){//后面所有需要YuanZhen对象的地方,都在这里提供return new YuanZhen();}
}

3,在application中创建component 保证component是唯一的

public class MyApplication extends Application {private MyComponent myComponent;@Overridepublic void onCreate() {super.onCreate();myComponent= DaggerMyComponent.builder().yuanZhenModule(new YuanZhenModule()).build();}public MyComponent getMyComonent(){return myComponent;}
}

4,验证:

public class MainActivity extends AppCompatActivity {@InjectYuanZhen yuanZhen;@InjectYuanZhen yuanZhen2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);((MyApplication)getApplication()).getMyComonent().injectMainActivity(this);System.out.println("yz------"+yuanZhen.hashCode());System.out.println("yz------"+yuanZhen2.hashCode());}}

输出:

六,多个component组合依赖 

如果我们一个类有好几个对象需要依赖的话,dagger2是不能使用多个Component同时注入同一个类中的,这种情况需要进行Component的组合

新建一个object对象:

public class YuanZhen2 {
}

创建新的注解:

@Scope
@Documented
@Retention(RUNTIME)
public @interface AppScope2 {
}

创建module:

@AppScope2
@Module
public class YuanZhen2Module {@AppScope2@Providespublic CuiJing provideYuanZhen2(){return new YuanZhen2();}
}

创建component:

@AppScope2
@Component(modules = {YuanZhen2Module.class})
public interface YuanZhen2Component {YuanZhen2 providerYuanZhen2();
}

进行依赖:

@AppScope
@Component(modules = {YuanZhenModule.class},dependencies = {YuanZhen2Component.class})
public interface MyComponent {void injectMainActivity(MainActivity mainActivity);
}

使用:

public class MainActivity extends AppCompatActivity {@InjectYuanZhen yuanZhen;@InjectYuanZhen2 yuanZhen2;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//    DaggerMyComponent.create().injectMainActivity(this);DaggerMyComponent.builder().yuanZhenModule(new YuanZhenModule()).yuanZhen2Component(DaggerYuanZhen2Component.create()).build().injectMainActivity(this);System.out.println("yz----"+yuanZhen.hashCode());System.out.println("yz----"+yuanZhen2.hashCode());}}

输出:

注意:

1.多个component上面的scope不能相同

2.没有scope的组件不能去依赖有scope的组件

七,总结

dagger2源码使用到的主要是APT框架,工厂模式和builder模式,dagger2的使用较为复杂,但是现在很多主流app都在使用,所以掌握还是很有必要的。

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

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

相关文章

4.Spring源码解析-loadBeanDefinitions(XmlBeanDefinitionReader)

第一个点进去 发现是空 肯定走的第二个逻辑了 这里在这里已经给属性设置了值&#xff0c;所以肯定不是空能拿到。 1.ClassPathXmlApplicationContext 总结&#xff1a;该loadBeanDefinitions是XmlBeanDefinitionReader设置xml文件在哪。

M3VSNET:无监督多度量多视图立体视觉网络(2021年)

M3VSNET&#xff1a;无监督多度量多视图立体视觉网络&#xff08;2021年&#xff09; 摘要1 引言2 相关工作3 实现方法3.1 网络架构 B. Huang, H. Yi, C. Huang, Y. He, J. Liu and X. Liu, “M3VSNET: Unsupervised Multi-Metric Multi-View Stereo Network,” 2021 IEEE Inte…

轻巧高效的剃须好工具,DOCO黑刃电动剃须刀上手

剃须刀大家都用过&#xff0c;我比较喜欢电动剃须刀&#xff0c;尤其是多刀头的悬浮剃须刀&#xff0c;感觉用起来很方便&#xff0c;剃须效率也很高。最近我在用一款DOCO小蔻的黑刃电动剃须刀&#xff0c;这款剃须刀轻巧易用&#xff0c;而且性价比超高。 相比于同类产品&…

Keil5 debug

目录 debug调试功能 基本功能&#xff1a; 程序复位&#xff1a;Reset 运行&#xff1a;Run 停止&#xff1a;Stop 断点调试&#xff08;Breakpoint Debugging&#xff09; 单步调试&#xff1a; 单步调试:Step 单步跳过调试&#xff1a;Step Over&#xff1a; 单步返…

Nginx-进程

Nginx-相关问题_01 Windows关闭所有nginx服务 windows 系统下开发调试时不用每次频繁的 启动->任务管理器->查找进程->结束进程&#xff01; 查看nginx的进程占用情况 tasklist | find /i "nginx.exe" || exit关闭nginx的所有进程 taskkill /im nginx.…

09. 智慧商城——订单结算、订单管理

01. 订单结算台 所谓的 “立即结算”&#xff0c;本质就是跳转到订单结算台&#xff0c;并且跳转的同时&#xff0c;需要携带上对应的订单参数。 而具体需要哪些参数&#xff0c;就需要基于 【订单结算台】 的需求来定。 (1) 静态布局 准备静态页面 <template><di…

2023-11-25 LeetCode每日一题(二叉树中的伪回文路径)

2023-11-25每日一题 一、题目编号 1457.二叉树中的伪回文路径二、题目链接 点击跳转到题目位置 三、题目描述 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中…

scipy 笔记:scipy.spatial.distance

1 pdist 计算n维空间中观测点之间的成对距离。 scipy.spatial.distance.pdist(X, metriceuclidean, *, outNone, **kwargs) 1.1 主要参数 X一个m行n列的数组&#xff0c;表示n维空间中的m个原始观测点metric使用的距离度量out输出数组。如果非空&#xff0c;压缩的距离矩阵…

SpringCloud之服务网关Gateway组件使用——详解

目录 一、网关介绍 1.什么是服务网关 2. 为什么需要网关 3.网关组件在微服务中架构 二、服务网关组件 1. zuul 1.x 2.x(netflix 组件) 1.1 zuul版本说明 2. gateway (spring) 2.1 特性 2.2 开发网关动态路由 2.2.1.创建项目引入网关依赖 2.2.2 快捷方式配置路由 2.2…

Vue3挂载完毕后,隐藏dom再重新加载组件的方法

组件原本是在PC端使用的&#xff0c;现在需要把组件再封装一次&#xff0c;供app调用&#xff0c;但是在app上会显示tag栏&#xff0c;有占位影响空间&#xff0c;所以需求去掉头部tag&#xff0c;只显示下方组件。 实现方法&#xff0c;以前是直接引用的组件&#xff0c;现在改…

简介vue

目录 一、介绍 渐进式框架​ 单文件组件​ 选项式 API (Options API)​ 组合式 API (Composition API)​ 该选哪一个&#xff1f;​ 创建一个 Vue 应用 应用实例​ 根组件​ DOM 中的根组件模板 应用配置​ 多个应用实例​ 一、介绍 Vue (发音为 /vjuː/&#xff…

.NET生成微信小程序推广二维码

前言 对于小程序大家可能都非常熟悉了&#xff0c;随着小程序的不断普及越来越多的公司都开始推广使用起来了。今天接到一个需求就是生成小程序码&#xff0c;并且与运营给的推广图片合并在一起做成一张漂亮美观的推广二维码&#xff0c;扫码这种二维码就可以进入小程序。为了…

【开源】基于Vue.js的大学计算机课程管理平台的设计和实现

项目编号&#xff1a; S 028 &#xff0c;文末获取源码。 \color{red}{项目编号&#xff1a;S028&#xff0c;文末获取源码。} 项目编号&#xff1a;S028&#xff0c;文末获取源码。 目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 实验课程档案模块2.2 实验资源模块2…

前端环境变量释义process.env与import.meta.env

视频教程 彻底搞懂前端环境变量使用和原理&#xff0c;超清楚_哔哩哔哩_bilibili 添加命令行参数 --modexxxxx 新建.env.xxxx文件,其中.env文件会在所有环境下生效 以VITE_开头&#xff0c;字符串无需加双引号 使用import.meta.env.VITE_xxxxx进行调用

【数据结构】树与二叉树(廿四):树搜索指定数据域的结点(算法FindTarget)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲3. 搜索指定数据域的结点a. 算法FindTargetb. 算法解析c. 代码实现a. 使用指向指针的指针b. 直接返回找到的节点 4. 代码整合 5.3.1 树的存储结构 5.…

C++之模版初阶(简单使用模版)

前言 在学习C的模版之前&#xff0c;咱们先来说一说模版的概念&#xff0c;模版在我们的日常生活中非常常见&#xff0c;比如我们要做一个ppt&#xff0c;我们会去在WPS找个ppt的模版&#xff0c;我们只需要写入内容即可&#xff1b;比如我们的数学公式&#xff0c;给公式套值&…

【Linux】基本指令(二)

本文续接上文基本指令&#xff08;一&#xff09; 目录 cpmvcatmore && less cp 语法&#xff1a;cp [选项] 源文件或目录 目标文件或目录 功能: 复制文件或目录 说明: cp指令用于复制文件或目录&#xff0c;如同时指定两个以上的文件或目录&#xff0c;且最后的目的地…

第一节HarmonyOS DevEcoStudio工具下载以及环境搭建

一、下载与安装DevEco Studio 在HarmonyOS应用开发学习之前&#xff0c;需要进行一些准备工作&#xff0c;首先需要完成开发工具DevEco Studio的下载与安装以及环境配置。 进入DevEco Studio 工具下载官网&#xff1a;https://developer.harmonyos.com/cn/develop/deveco-stu…

经典滑动窗口试题(二)

&#x1f4d8;北尘_&#xff1a;个人主页 &#x1f30e;个人专栏:《Linux操作系统》《经典算法试题 》《C》 《数据结构与算法》 ☀️走在路上&#xff0c;不忘来时的初心 文章目录 一、水果成篮1、题目讲解2、讲解算法思路3、代码实现 二、找到字符串中所有字母异位词1、题目…

距离向量路由协议——IGRP和EIGRP

IGRP-内部网关路由协议 IGRP&#xff08;Interior Gateway Routing Protocol&#xff0c;内部网关路由协议&#xff09;是一种动态距离向量路由协议&#xff0c;它是Cisco公司在20世纪80年代中期设计的&#xff0c;是Cisco专用路由协议。目前在Cisco高版本的IOS已经对IGRP不提…