Android单排上王者系列之Dagger2使用解析

2023-11-02

转自:http://blog.csdn.net/study_zhxu/article/details/52169090

本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

前言

现在Dagger2在项目中的使用越来越多,Dagger2是Dagger的升级版本,Dagger没有使用过,但是本篇说的是Dagger2,主要讲解的是Dagger2是如何使用的。对了,忘了说Dagger其实是一个依赖注入的框架。

什么是依赖注入

依赖注入是一种面向对象的编程模式,它的出现是为了降低耦合性,所谓耦合就是类之间依赖关系,所谓降低耦合就是降低类和类之间依赖关系。可能有的人说自己之前并没有使用过依赖注入,其实真的没有使用过吗?当我们在一个类的构造函数中通过参数引入另一个类的对象,或者通过set方法设置一个类的对象其实就是使用的依赖注入。

通常依赖注入有以下几种方式

  • 通过接口注入
  • interface ClassBInterface {
        void setB(ClassB b);
    }
    public class ClassA implements ClassBInterface {
        ClassB classB;
        @override
        void setB(ClassB b) {
        classB = b;
        }
    }

  • 通过set方法注入
  • public class ClassA {
        ClassB classB;  
        public void setClassB(ClassB b) {
          classB = b;
        }
    }

  • 通过构造方法注入
public class ClassA {
    ClassB classB;
    public void ClassA(ClassB b) {
        classB = b;
    }
}
  • 通过注解的方式注入
public class ClassA {
    //此时并不会完成注入,还需要依赖注入框架的支持,如Dagger2
    @inject 
    ClassB classB;
    public ClassA() {}
}

下面我们就来说说如何通过Dagger2来实现依赖注入吧。

引入Dagger2

添加apt插件

dependencies {
        classpath 'com.android.tools.build:gradle:2.1.2'
        //添加apt插件
        classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
    }

添加依赖(在build.gradle中添加如下代码)

apply plugin: 'com.android.application'
    //添加如下代码,应用apt插件
    apply plugin: 'com.neenbedankt.android-apt'
    ...
    dependencies {
    ...
    compile 'com.google.dagger:dagger:2.4'
    apt 'com.google.dagger:dagger-compiler:2.4'
    //java注解
    compile 'org.glassfish:javax.annotation:10.0-b28'
    ...
}

使用Dagger2

添加完Dagger的依赖后我们如何在项目中使用Dagger呢?

在项目中绝大多数的使用都是Dagger结合MVP架构使用的,在MVP中使用是非常典型的降低耦合的使用。不懂MVP的可以看这里
本篇文章中的示例是一个简单的登陆功能的示例,代码沿用上篇讲解MVP的登陆代码,看这里,该示例采用MVP架构设计通过Dagger2进行解耦合,下面就来看看如何使用吧。

在使用Dagger2前我们最好简单的了解一下MVP,主要是为了理解本篇中的代码。简单了解MVP即使不会写MVP也可以看的懂本篇的代码。

为什么要选择在MVP模式中使用Dagger2呢?因为在MVP模式中Activity持有presenter的引用,同时presenter也持有view的引用,这样便于更新UI界面,这样Activity就和presenter仅仅的耦合在一起了,
而Dagger2是依赖注入框架就是解耦合的,所以子MVP中使用Dagger2也就再好不过了。
在上篇文章讲解MVP时我们可以明显的看到如下代码

public class LoginActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{
    private Button mLogin ;
    private Button mClear ;
    private EditText mName ;
    private EditText mPassWord ;
    ILoginPresenter loginPresenter ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mLogin = (Button) findViewById(R.id.btn_login);
        mClear = (Button) findViewById(R.id.btn_clear);
        mName = (EditText) findViewById(R.id.et_name);
        mPassWord = (EditText) findViewById(R.id.et_password);

        mLogin.setOnClickListener(this);
        mClear.setOnClickListener(this);

        //持有presenter的引用并且创建对象
        loginPresenter = new LoginPresenterCompl(this) ;

    }
    ........
}

在上述代码中可以看到activity持有了presenter的引用并且创建了该对象,但是如果presenter的构造函数发生改变则这里也需要改变,其实所有和presenter构造函数相关的代码都要改变。

但是如果我们使用Dagger2依赖框架该如何使用呢?请看下面代码

activity中的代码

public class LoginActivity extends AppCompatActivity implements ILoginView,View.OnClickListener{

    ..........

    //注意此处使用了注解
    @Inject
    LoginPresenterCompl loginPresenter ;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mLogin = (Button) findViewById(R.id.btn_login);
        mClear = (Button) findViewById(R.id.btn_clear);
        mName = (EditText) findViewById(R.id.et_name);
        mPassWord = (EditText) findViewById(R.id.et_password);
        mLogin.setOnClickListener(this);
        mClear.setOnClickListener(this);
        DaggerMainComponent.builder().mainModule(new MainModule(this)).build().inject(this);

    }
    .......
}

LoginPresenterCompl中的代码

public class LoginPresenterCompl implements ILoginPresenter {
        private ILoginView loginView ;
        private User user ; 
        //注意此处使用了注解
        @Inject
        public LoginPresenterCompl(ILoginView view){
            loginView = view ;
            user = new User("张三","123456") ;
        }
        ......
    }

只有上述两个注解还无法完成依赖注入,还需要如下两个新增类

新增的MainModule类

@Module
public class MainModule {
    private final ILoginView view ;
    public MainModule(ILoginView view){
        this.view = view ;
    }
    @Provides
    ILoginView provideILogView(){
        return view ;
    }
}

新增的MainComponent接口

@Component(modules = MainModule.class)
public interface MainComponent {
    public void inject(LoginActivity activity) ;
}

通过直接注解和上述两个接口类即可完成Dagger2的依赖注入。
在LoginActivity中是通过

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

完成依赖注入的。

看完上面的代码后,一脸的懵逼,WTF(what the fuck),这TM是什么,这么复杂,还不如之前的简单呢,新增了两个类还有这么多代码,得不偿失呀!

同志们,如果你们第一眼看到后是这样想的话,说明和我想的一样,呵呵。每一个刚接触Dagger2的人可能都会这样想,因为我们只看到了表面。

不错,表面上我们是多了一个类和接口也多了很多代码,但是这样的组合其实是可以理解的。因为通常简单的代码具有耦合性,而要想降低这样的耦合就需要其他的辅助代码,其实少代码量和低耦合这两者并不能同时兼顾,古人云:鱼和熊掌不可兼得。我们作为堂堂聪明绝顶的程序猿怎么可能会输给古人呢。

好!下面来认真讲解Dagger2是如何完成依赖注入的。

首先我们来看看LoginActivity代码

LoginActivity中有这么一段代码

@Inject
LoginPresenterCompl loginPresenter ;

同样在LoginPresenterCompl中也有这么一段代码

@Inject
public LoginPresenterCompl(ILoginView view){
    loginView = view ;
    user = new User("张三","123456") ;
}

之所以挑出这两段代码是因为它们都添加了@Inject注解。

在LoginActivity中其实只有这么一句提到loginPresenter,在接下来的代码中并没有对其进行初始化。
那loginPresenter是如何进行初始化的呢(此处注意添加@Inject注解的变量不能被private修饰)?

直观上我们可以这样理解,被@Inject注解的代码存在某种联系,当代码执行到@Inject的时候程序会自动进入到这个类的构造方法中,如果正巧这个构造方法也被@Inject修饰了,那么系统就会帮我们自动创建对象。

这只是表面的理解,这其中肯定还有很多我们没有看到的“猫腻”。这俩不会无缘无故的有联系,肯定还有第三者,通过这个第三者这两个被@Inject注解修饰的代码才会产生联系。

这个第三者是谁呢?自然的我们就会想到我们添加的这个类和接口。

首先我们来分析MainComponent接口

代码如下

@Component(modules = MainModule.class)
public interface MainComponent {
    public void inject(LoginActivity activity) ;
}

MainComponent是一个接口(也可以是一个抽象类),在这个接口中我们定义了一个inject()方法,其中参数是LoginActivity对象,同时MainComponent还被@Component注解着,注解中modules的值是MainModule.class,这个内容会在接下来的地方进行说明,暂时先放一放。

此时在Android studio中,如果我们rebuild的一下项目就会有新的发现。在项目的build/generated/source/apt/debug/项目包名/dragger目录下生成对应的包其中包含DaggerMainComponent类,这个类名其实不是固定的,是根据我们上面写的MainComponent,加了Dagger前缀生成的DaggerMainComponent。
其实在这个时候我们就已经完成了present的依赖注入。

但是在

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

中我们看到还有一个MainModule,这个是我们自己创建的一个类

MainModule代码如下

@Module
public class MainModule {
    private final ILoginView view ;
    public MainModule(ILoginView view){
        this.view = view ;
    }
    @Provides
    ILoginView provideILogView(){
        return view ;
    }
}

我们可以看到这个类被@Module注解修饰,内部有一个ILoginView的变量和一个构造方法还有一个被@Provides修饰的provideILogView方法。

看到这还是一脸懵逼,这个类是干嘛的?

在MainComponent接口中我们看到这么一个注解@Component(modules = MainModule.class),这里用到了MainModule,可见MainComponent需要MainModule一起才能完成工作。

其实这个类我们可以理解成提供参数的,也就是提供参数依赖的,如何理解呢?

在MainModule中我们为什么要提供ILoginView类型的对象?为什么不是其他的呢?
这是因为LoginPresenterCompl的构造函数需要这么一个参数,所以我们在这里提供这么一个相同的参数,并通过被@Provides注解修饰的方法将其返回出去,如果LoginPresenterCompl还需要其他的参数,同样我们也可以在这里添加对应类型的参数然后通过另一个被@Provides注解修饰的方法返回出去。

在MainComponent接口中提供的inject()方法的参数是LoginActivity,这个参数的含义是LoginPresenter要在什么地方注入。

了解了各个类的功能后我们来总结一下

  • @Inject 程序会将Dagger2会将带有此注解的变量或者构造方法参与到依赖注入当中,Dagger2会实例化这个对象

  • @Module 带有该注解的类需要对外提供依赖,其实就是提供实例化需要的参数,Dagger2在实例化的过程中发现一些参数,Dagger2就会到该类中寻找带有@Provides注解的以provide开头的需找对应的参数

  • @Component 带有该注解的接口或抽象类起到一个关联桥梁的作用,作用就是将带有@Inject的方法或对象和带有@Module的类进行关联,只有通过该接口或抽象类才可以在实例化的时候到带有
    @Module中类中去寻找需要的参数,也就是依赖注入。

OK,下面我们来捋捋思路。

  • 1、在这个示例代码中,LoginActivity中需要LoginPresenterCompl,所以在LoginActivity中定义了该对象并且通过@Inject将其注解,同时到LoginPresenterCompl的构造方法中也通过@Inject将其注解,
    表明这些是需要依赖注入的。

  • 2、因为在LoginPresenterCompl的构造方法需要ILoginView类型的参数,所以需要通过依赖将获取这些参数,所以就需要带有@Module注解的类用于获取需要的参数,
    在@Module注解的类中通过被@Provides注解的以provide开头的方法对外提供需要的参数,一般而言有几个参数就需要有几个带有@Provides的方法。

  • 3、此时还需要一个桥梁将两者联系到一起,带有@Component的接口或抽象类就起到这个桥梁的作用。注解中有一个module的值,这个值指向需要依赖的Module类,同时其中有一个抽象方法
    inject(),其中的参数就是我们需要在哪个类中实例化LoginPreserentCompl,因为我们需要在LoginActivity中实例化,所以参数类型就是LoginActivity类型。
    然后在Android studio中rebuild我们的项目,就会生成DaggerMainComponent类,通过

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

完成我们需要的依赖注入。

总结

可能我们通过上面的讲解,知道了如何使用Dagger2了,也知道具体的流程了,但是可能还会有些疑惑,为什么?Dagger2是如何通过一些接口和类就完成依赖注入的?

在此声明,别着急,知道如何使用这只是第一步,在下一篇文章中将会讲解Dagger2实现依赖注入的原理。敬请期待!!!


本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android单排上王者系列之Dagger2使用解析 的相关文章

  • 接口MD5签名校验工具类

    签名算法过程 1 对除签名外的所有请求参数按key做ASCII升序排列 value无需编码 假设当前时间的时间戳是12345678 例如 有c 3 b 2 a 1 三个参 另加上时间戳后 按key排序后为 a 1 b 2 c 3 times
  • 安装启动引导器的设备(Device for boot loader installation):

    选择sdXY 第一个X是一个字母 表示磁盘个数 第二个Y是一个阿拉伯数字 代表分区 的那一项 也就是说 要将启动引导器安装在 分区中 dev xyzn dev 这个字串是所有设备文件所在的目录名 因为分区在硬盘上 而硬盘是设备 所以这些文件
  • async和await的讲解

    async和await的讲解 声明async函数的几个方法 普通的函数声明 async function A 声明一个函数表达式 let A async function async形式的箭头函数 let A async gt 初识asyn
  • sklearn Pipeline 函数用法

    0 导入包 from sklearn pipeline import Pipeline 1 定义 Pipeline 中文是管道 相当于将一系列的操作封装成一个函数 可以拿这个函数对其他数据进行相同的 流水线 操作 class sklearn
  • C++ linux下使用X11实现屏幕截图(续)

    使用libpng库将XGetImage获取到的内容保存为PNG格式的文件 以下是一个示例代码 include
  • PCL 计算点云的马氏距离(C++详细过程版)

    目录 一 算法概述 二 代码实现 三 结果展示 一 算法概述 计算每个点到最近邻点的马氏距离 最后根据马氏距离进行颜色渲染 马氏距离的详细介绍见 百度百科 马氏距离 二 代码实现 include
  • C# 数组增加元素_C#教程推荐

    C 是微软公司发布的一种面向对象的 运行 NET Framework和 NET Core 完全开源 跨平台 之上的高级程序设计语言 C 看起来与Java有着惊人的相似 它包括了诸如单一继承 接口 与Java几乎同样的语法和编译成中间代码再运
  • HDU - 3789 奥运排序问题(暴力)

    按要求 给国家进行排名 Input 有多组数据 第一行给出国家数N 要求排名的国家数M 国家号从0到N 1 第二行开始的N行给定国家或地区的奥运金牌数 奖牌数 人口数 百万 接下来一行给出M个国家号 Output 排序有4种方式 金牌总数
  • FastDFS上传文件失败, item “tracker_server“ in ***/fast_client.conf not found

    上传测试时报了一个这样的错 Exception in thread main net dopan fastdfs client common MyException item tracker server in F Study Java 0
  • PhpStorm 快捷键大全 PhpStorm 常用快捷键和配置

    PhPStorm 是 JetBrains 公司开发的一款商业的 PHP 集成开发工具 PhpStorm可随时帮助用户对其编码进行调整 运行单元测试或者提供可视化debug功能 Phpstrom的一款名为Magicento的插件对快速创建Ma

随机推荐

  • 关于Ext内存泄漏的部分心得

    首先说明下 本帖所描述的泄漏是指JS运行中的释放问题 它们大多数在页面关闭时都能释放掉 内存释放在使用Ext开发OPOA系统时显得尤为重要 去年开始接触Ext开发 中间花了很多时间来解决内存泄漏 最开始Ext还是3 0版 泄漏得一塌胡涂 也
  • 【Qt】QtIFW 安装包制作总结-通用打包过程

    QtIFW 安装包制作总结 一 引言 当一个软件开发完成后 需要发布 面对发布 故而需要制作一个安装器 将其软件运行本体打包 并可根据软件运行的平台进行相关平台参数的定制 从而实现软件的安装 一个软件包安装器 可能包含以下几个部分 本系列文
  • 萝莉遥控乐高电机

    文章背景 前段时间买了一辆积木拼搭的玩具车 春节几天把他组装好了 拥有遥控控制转向以及前进后退的功能 但自带的电池以及遥控感觉不太好用 特别是电池 充电两小时 跑起来五分钟 太不耐用了 自带遥控器 接收端 积木的接口都是统一的 这样的好处是
  • 代码运行出现:No module named ‘torch_geometric‘

    这是没有torch geometric库导致的 但是不能像一般库安装 pip install 库名 或者 conda install 库名 进行安装 经常会报错 需要先安装四个小部件再安装torch geometric 具体安装步骤如下 s
  • java-遗漏知识

    一 JVM Java中JVM虚拟机详解 https blog csdn net sinat 35512245 article details 54744815 二 JVM web项目中的跨 2 一 JVM web项目中的跨 一 JVM we
  • 通过live555实现H264 RTSP直播

    转载自 http blog csdn net firehood article details 16844397 前面的文章中介绍了 H264视频通过RTMP流直播 下面将介绍一下如何将H264实时视频通过RTSP直播 实现思路是将视频流发
  • centos8 安装docker与(containerd.io与podman-manpages问题解决)及镜像加速

    docker安装 环境说明 windows系统安装virtualbox 并在virtualbox环境中centos8系统 如果以下操作都基于centos系统root用户执行的 不需要加在命令前面sudo 1 卸载老版本 sudo yum r
  • Linux 面试题

    1 什么是Linux的内核 作用是 Linux 内核是 Linux 操作系统的核心组件 它是操作系统的底层软件 负责管理计算机的硬件资源 并提供了一些基本的系统服务 内核是操作系统与硬件之间的桥梁 它处理系统中的进程调度 内存管理 设备驱动
  • Linux系统搜索某个目录下特定文件的方法(find)

    在Linux下搜索指定名称的文件的方法 可以用find命令 格式 find
  • idea快速上手指南

    安装 双击打开安装包 选择一个目录 最好不要中文和空格 然后选择桌面快捷方式 请选择64位 然后选择安装 开始安装 然后勾选安装后运行 Finish 首次配置 然后是UI界面选择 有白色和黑色两款 总有一款适合你 把不需要的组件禁用 插件暂
  • IDEA Tomcat 控制台乱码

    解决方案 找到 logging properties 文件 修改控制台日志处理器编码格式 找到java util logging ConsoleHandler encoding 将其值修改为 java util logging Consol
  • 机器学习笔记4 - 神经网络

    机器学习笔记4 神经网络 线性回归和逻辑回归都有一个缺点 当特征太多 计算负荷会很大 引入神经网络 在神经网络中 参数称为权重 weight 其中 x 1 x 2
  • 项目实训(一)前端框架的搭建

    目录 一 搭建前端框架 二 导入js和css样式框架 控制样式 三 初始化网页结构 一 router设置 二 index vue 三 导航栏 四 回到顶部按钮 一 搭建前端框架 首先选定使用的前端框架为Vue 在配置文件main js中导入
  • 【前端】ant-design-pro初体验

    什么是Ant Design Pro Ant Design Pro 是一个企业级中后台前端 设计解决方案 它秉承 Ant Design 的设计价值观 致力于在设计规范和基础组件的基础上 继续向上构建 提炼出典型模板 业务组件 配套设计资源 进
  • linux文件目录命令

    文件目录1 pwd 显示当前工作目录的绝对路径 print working directory ls 显示文件目录 a 显示所有的文件 l 以列表的形式显示 h 数据以xxxk的形式显示 F 对于是目录的文件后面加上 cd 切换到指定的目录
  • Windows网络编程基础(一)

    Table of Contents 准备工作 socket C S模式 源代码 服务端 Server cpp 客户端 Client cpp 源码分析 数据传输 关闭连接 准备工作 Windows网络编程一般是指 Windows Socket
  • Android Device Monitor运行报错解决方案

    我用的jdk17和jdk11的话 版本太高了 DDMS会报错 报错信息如下 SESSION 2023 08 27 21 27 08 420 eclipse buildId unknown java version 17 0 6 java v
  • 暗影精灵电脑 开机速度慢的解决方法

    文章目录 问题现象 发生机理 解决方案 问题现象 惠普暗影精灵笔记本在开机前有一个特别长的黑屏有背光的时间 过了这个时间才能看到暗影精灵的logo 表示Windows开始加载 这导致暗影精灵3笔记本的开机时间接近一分钟 这在SSD大行其道的
  • SpringCloud项目Nacos替换Eureka(一)

    Nacos替换Eureka记录 最近公司做各个系统的Eureka替换Nacos注册中心 因为各个系统以前规划问题 SpringCloud版本存在差异性 所以需要根据当前版本的SpringCloud对照SpringCloudAlibaba版本
  • Android单排上王者系列之Dagger2使用解析

    转自 http blog csdn net study zhxu article details 52169090 本篇文章已授权微信公众号 guolin blog 郭霖 独家发布 前言 现在Dagger2在项目中的使用越来越多 Dagge