Dagger2的使用以及原理分析

2023-11-18

使用

Dagger2的使用说起来并不难,关键在于要掌握Dagger2的提供的几个注解及其意思。

环境搭建

在模块级的build.gradle文件中加入如下依赖:

plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'kotlin-kapt'
}

android {
    namespace 'com.example.test'
    compileSdk 32

    defaultConfig {
        applicationId "com.example.test"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
}

dependencies {

    implementation 'androidx.core:core-ktx:1.7.0'
    implementation 'androidx.appcompat:appcompat:1.4.1'
    implementation 'com.google.android.material:material:1.5.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
    //dagger2依赖
    implementation 'com.google.dagger:dagger:2.44.2'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.44.2'//看到这句话,毫无疑问,dagger2使用apt技术
    //kotlin语言需要使用kapt
    kapt 'com.google.dagger:dagger-compiler:2.44.2'
}

注解

  • @Inject
    如果在类上添加此依赖注入,Dagger 就会构造一个这个类的实例并满足他们的依赖。
    通过这个inject注解可以将依赖需求方对象送到Component类中,Component类就会根据依赖需求方对象中声明的依赖关系来注入依赖需求方对象中所需要的对象,注意:inject方法的参数不能用父类来接收,@Inject注解的字段不能是private和protected的。

  • @Module
    编写Module类时要在该类上声明@Module以表明该类是Module类,这样Dagger2才能识别,Modules 类里面的方法专门提供依赖,如返回你需要依赖的对象实例。

  • @Provide
    在 modules 中我们定义的方法就用@Provide注解,作用是声明Module类中哪些方法是用来提供依赖对象的,当Component类需要依赖对象时,他就会根据返回值的类型来在有@Provides注解的方法中选择调用哪个方法。

  • @Singleton
    实现单例

  • @Component
    Components 从根本上来说就是一个注入器,也可以说是@Inject 和@Module 的桥梁,来连接@Inject 和@Module这两个部分。但@Component注解的作用可不是单单用来声明Component类,@Component注解有modules和dependencies两个属性,这两个属性的类型都是Class数组,modules的作用就是声明该Component含有哪几个Module;而dependencies属性则是声明Component类的依赖关系。

可能大家现在不太清楚上面几个注解的作用,看下面几个案例大家就知道了。

案例:正常情况下,如果我们想在要一个类中使用另一个类的对象,肯定需要new一下吧。

package com.example.test

class Student {

}

package com.example.test;

public class StudentManager {

    private Student student;
    private static StudentManager manager;

    public StudentManager(){
        student = new Student();
    }

    public static StudentManager getManager(){
        if(manager==null){
            manager = new StudentManager();
        }
        return manager;
    }

    public Student getStudent(){
        return student;
    }
}

package com.example.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Toast.makeText(this,"hashcod is ${StudentManager.getManager().student.hashCode()}",Toast.LENGTH_LONG).show()
    }
}

在上例中,我们在StudentManager 类中new了一个Student的对象,这样我们在MainActivity中才能调用student类的hashcode方法,否则分分钟就会报空指针异常。那么有没有可能我们不需要在StudentManager类中new出Student对象,而student自动被初始化呢?Dagger2其实就是替我们完成这个初始化的步骤。也就是我们常说的“依赖注入”—>顾名思义,依赖Dagger2帮我们new出Student对象,然后注入到StudentManager中去。
那么,我们该如何使用Dagger2呢?牢记下面几个步骤即可:

  1. 将你要注入的对象放入一个Module类当中,该Module类必须被Module注解注释,同时要提供一个被Provides注解注释方法,获取注入对象的实例。什么意思呢?以下面的案例为例,我们要注入的对象是Student类,那么我们提供一个StudentModule类,然后在getStudnet方法中new出Student对象。
package com.example.test

import dagger.Module
import dagger.Provides

@Module
class StudentModule {

    @Provides//如果不使用该注解,dagger框架就不知道如何new出Student对象了
    fun getStudnet():Student{
        return Student()
    }
}
  1. 新建一个接口类,使用Component注解注释,这样dagger2的注解处理器会帮助我们生成一个DaggerStudentComponent方法
package com.example.test

import android.app.Activity
import dagger.Component

@Component(modules = [StudentModule::class])
interface StudentComponent {

//易错点:1.必须要要有一个注入地点的参数,例如:我们想要注入到StudentManager中,那么必须传该类的参数
//2.参数类型必须是本类,不能是其父类
//不符合上面两点,都是会报空指针异常的
    fun injectActivity(activity: MainActivity)//返回值必须是void

    fun inject(str:StudentManager)
}

  1. 在需要注入的地方使用inject注解和DaggerStudentComponent类
package com.example.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Toast
import javax.inject.Inject

class MainActivity : AppCompatActivity() {

    @Inject
    lateinit var student:Student

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        DaggerStudentComponent.create().injectActivity(this)
        Toast.makeText(this,"hashcode is ${student.hashCode()}",Toast.LENGTH_LONG).show()
        Toast.makeText(this,"hashcod is ${StudentManager.getManager().student.hashCode()}",Toast.LENGTH_LONG).show()
    }

    val verticalGradientBrush = Brush.verticalGradient(
        colors = listOf(Color.Red,Color.Yellow,Color.White)
    )
}

这样即可使用dagger2框架帮助我们自动初始化对象。
现在,我们有一个疑问,在Android中如果不是四大组件,只是一个普通的类能不能使用dagger2呢?答案是可以的。例如:我们在StudentMananger中初始化Student类。

package com.example.test;

import javax.inject.Inject;

public class StudentManager {

    @Inject
    public Student student;
    private static StudentManager manager;

    public StudentManager(){
//        Activity activity;
        DaggerStudentComponent.create().inject(this);
//        student = new Student();
    }

    public static StudentManager getManager(){
        if(manager==null){
            manager = new StudentManager();
        }
        return manager;
    }

    public Student getStudent(){
        return student;
    }
}

原理分析

dagger2的用法我们明白了之后,接下来我们来分析下dagger2的原理。首先入手的地方肯定就是DaggerStudentComponent这个类了。代码比较简短,笔者就直接copy过来分析了。DaggerStudentComponent这个类是怎么来的呢?当我们使用Component注解时,注解处理器会自动帮我们生成。这块大家可以查阅dagger2的源码。我们调用了create方法之后就会生成StudentComponentImpl类,该类实现了我们定义的StudentComponent接口。因此,我们调用的inject方法或injectActivity方法,其实都是调用了StudentComponentImpl类中的方法。接下来,我们具体看看StudentComponentImpl中的方法都做了什么事情。

// Generated by Dagger (https://dagger.dev).
package com.example.test;

import dagger.internal.DaggerGenerated;
import dagger.internal.Preconditions;

@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class DaggerStudentComponent {
  private DaggerStudentComponent() {
  }

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

  public static StudentComponent create() {
    return new Builder().build();
  }

  public static final class Builder {
    private StudentModule studentModule;

    private Builder() {
    }

    public Builder studentModule(StudentModule studentModule) {
      this.studentModule = Preconditions.checkNotNull(studentModule);
      return this;
    }

    public StudentComponent build() {
      if (studentModule == null) {
        this.studentModule = new StudentModule();
      }
      return new StudentComponentImpl(studentModule);
    }
  }

  private static final class StudentComponentImpl implements StudentComponent {
    private final StudentModule studentModule;

    private final StudentComponentImpl studentComponentImpl = this;

    private StudentComponentImpl(StudentModule studentModuleParam) {
      this.studentModule = studentModuleParam;

    }

    @Override
    public void injectActivity(MainActivity activity) {
      injectMainActivity(activity);
    }

    @Override
    public void inject(StudentManager str) {
      injectStudentManager(str);
    }

    private MainActivity injectMainActivity(MainActivity instance) {
      MainActivity_MembersInjector.injectStudent(instance, StudentModule_GetStudnetFactory.getStudnet(studentModule));
      return instance;
    }

    private StudentManager injectStudentManager(StudentManager instance) {
      StudentManager_MembersInjector.injectStudent(instance, StudentModule_GetStudnetFactory.getStudnet(studentModule));
      return instance;
    }
  }
}

我们分析下injectMainActivity这个方法,injectStudentManager是一样的逻辑。MainActivity_MembersInjector这个类和StudentModule_GetStudnetFactory这个类,自然也是inject和module注解通过注解处理器帮我们生成。

// Generated by Dagger (https://dagger.dev).
package com.example.test;

import dagger.MembersInjector;
import dagger.internal.DaggerGenerated;
import dagger.internal.InjectedFieldSignature;
import dagger.internal.QualifierMetadata;
import javax.inject.Provider;

@QualifierMetadata
@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class MainActivity_MembersInjector implements MembersInjector<MainActivity> {
  private final Provider<Student> studentProvider;

  public MainActivity_MembersInjector(Provider<Student> studentProvider) {
    this.studentProvider = studentProvider;
  }

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

  @Override
  public void injectMembers(MainActivity instance) {
    injectStudent(instance, studentProvider.get());
  }

  @InjectedFieldSignature("com.example.test.MainActivity.student")
  //这里,终于明白为什么我们需要传入该类的对象过来了,不然怎么为其赋值呢?
  public static void injectStudent(MainActivity instance, Student student) {
    instance.student = student;
  }
}

这个类,没什么好说的就是提供Student对象而已。

// Generated by Dagger (https://dagger.dev).
package com.example.test;

import dagger.internal.DaggerGenerated;
import dagger.internal.Factory;
import dagger.internal.Preconditions;
import dagger.internal.QualifierMetadata;
import dagger.internal.ScopeMetadata;

@ScopeMetadata
@QualifierMetadata
@DaggerGenerated
@SuppressWarnings({
    "unchecked",
    "rawtypes"
})
public final class StudentModule_GetStudnetFactory implements Factory<Student> {
  private final StudentModule module;

  public StudentModule_GetStudnetFactory(StudentModule module) {
    this.module = module;
  }

  @Override
  public Student get() {
    return getStudnet(module);
  }

  public static StudentModule_GetStudnetFactory create(StudentModule module) {
    return new StudentModule_GetStudnetFactory(module);
  }

  public static Student getStudnet(StudentModule instance) {
    return Preconditions.checkNotNullFromProvides(instance.getStudnet());
  }
}

至此,我们的源码分析到此结束。至于,apt生成代码这块有兴趣的大家可以自行分析。掌握apt注解处理器的知识,看源码就会比较容易,没有掌握的读者也不用担心,可以参考笔者之前的一篇博客:APT和Javapoet的精彩联动
其实,很多高大上的框架,它的底层技术貌似都是apt、泛型、asm、反射、设计模式等这些基础的知识。因此,学好基础是十分重要嘀。。。

参考资料

Dagger2的简单使用

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

Dagger2的使用以及原理分析 的相关文章

随机推荐

  • ArcGISMapsSDK for UnrealEngine_Beta2_00

    ArcGISMapsSDK for UnrealEngine Beta2 00 Prepare 1 Esri Community 2 All Communities 3 ArcGIS Maps SDK for Unreal Engine 4
  • Swing可视化设计:在IntelliJ IDEA中安装JFormDesigner教程

    Swing可视化设计 在IntelliJ IDEA中安装JFormDesigner教程 前言 最近课设大多需要gui设计 排除自学的情况 大部分同学都只接触过swing设计gui swing可视化插件将大大提高工作效率 这里提高破解版使用方
  • 数据结构与算法【Java】02---链表

    前言 数据 data 结构 structure 是一门 研究组织数据方式的学科 有了编程语言也就有了数据结构 学好数据结构才可以编写出更加漂亮 更加有效率的代码 要学习好数据结构就要多多考虑如何将生活中遇到的问题 用程序去实现解决 程序 数
  • 微信小程序申请 wx.getLocation 接口 审核一直不通过

    项目需要通过微信的 getLocation 获取本地的位置信息 经纬度 但是提交很多次审核都不通过 最后通过写了个项目里用不到的 导航功能 截图录屏才通过了审核 以下申请文案及配图仅供参考 因当前业务涉及就近医院挂号取号业务 需获取用户地理
  • python 使用pip install 手动安装本地包的方法

    Installing pystan manually fixed the issue otherwise it would just hang forever GitHub git clone https github com facebo
  • 【js中的单元测试】【30秒快速入门】

    什么是单元测试 测试是一种验证我们代码是否可以按预期工作的方法 换种说法就是写些代码来验证一段代码的正确性 被测试的对象可以是样式 功能 流程 组件等 单元测试是对软件中最小可测试单元进行检测和验证 单元测试能有效的提升工作效率 1 能监测
  • 常见swap()函数实现和细节讲解

    前言说明 swap 函数的作用是进行交换传入的两个值 本文都以整形int举例说明 且用C语言描述 常见的swap的实现方式有三种 格外一个空间的临时存放发 无格外空间的位运算异或法 无额外空间的加减法 主程序框架 include
  • 使用HAL库开发STM32:UART进阶使用

    文章目录 目的 发送处理 存在的问题 解决方法 个人常用处理方式 数据接收与解析 数据接收 数据解析 对于HAL库的吐槽 总结 目的 在前面文章 使用HAL库开发STM32 UART基础使用 中介绍的UART的基础使用 基础使用非常简单 不
  • U盘插入电脑后,有提示音,但不能显示出来,如何解决?

    导致此类问题的原因可能是用户的失误操作或者病毒的恶意修改等 1打开我的电脑 U盘没有显示出来 2打开控制面板单击选择设备和打印机 3在设备那一栏里会发现如图中已点击的图标 如果你的U盘没有改名字的话默认就是这个名字 有些品牌点击的U盘显示的
  • daily-timeline.js——打造每日时间轴

    最近因为需要在做会议室预约系统 其中需要用到一个显示当天预约情况的时间轴 去网上找了一下 发现只有和微博类似的历史时间轴 于是便自己动手做了一个当日时间轴控件 daily timeline js 实际使用效果如下 原理是Canvas的绘制
  • c语言中strcat函数的作用

    原型 extern char strcat char dest char src 用法 include
  • 在浏览器地址栏中输入地址后浏览器发生了什么?

    文章目录 前言 一 DNS查询 二 TCP连接 三 发送HTTP请求 四 服务器处理HTTP请求并返回HTTP报文 五 浏览器解析并渲染页面 六 HTTP连接断开 前言 当我们向浏览器的地址栏中输入一个网址并按下enter键之后 便可以跳转
  • MySQL 删除表数据,重置自增 id 为 0 的两个方式

    MySQL 删除表数据 重置自增 id 为 0 的两个方式 1 truncate table table name truncate table user 2 delete 配合 alter 语句 delete from table nam
  • Notepad++编辑过的行颜色设置 LocationNavigate.ini设置 高亮 黄色 绿色 修改的行变为黄色 修改的行高亮显示

    使用NotePad 修改的行会显示为橘黄色 保存之后 则显示为绿色 这两种颜色太亮了 想把色值调低 于是 展开搜索 发现这个功能是插件Location Navigate 带的 之后 找到了配置文件C Users xx AppData Roa
  • 阿里服务器怎么用教程[第一部分]

    第一步 登录我们的阿里云账号 第二步 根据自己的具体情况 选择好服务器的配置 比如你是大型企业 预估网站访问量很大 那么就要选配置较好的服务器 如果是个人网站 预估流量较小 就可以选择配置较低的云服务器 第三步 购买好云服务器后 我们在阿里
  • 使用 Python 实现 Excel 自动化

    使用 Python 实现 Excel 自动化 从 excel 过渡到 python 并提高您的工作效率 此视频教程共8 0小时 中英双语字幕 画质清晰无水印 源码附件全 课程英文名 Excel Automation Using Python
  • Scoop包管理工具

    不同系统下包管理工具 系统 工具 范例 备注 Arch Linux Pacman pacman S pyenv Built in CentOS RHEL yum yum install python wheel Built in Debia
  • Cocos2d-x 3.9教程:10.使用CocosStudio的UI编辑器从UI文件中加载布局和控件

    Cocos2d x 3 9教程 10 使用CocosStudio的UI编辑器从UI文件中加载布局和控件 1 1 使用CocosStudio的UI编辑器 1 1 1 安装和启动 从官网上下载2015年11月18日版本 Cocos studio
  • 谷歌浏览器输入url地址后http自动转https问题解决方法

    谷歌浏览器输入 http 域名 后自动变成 https 域名 格式原因 安装配置了 SSL证书后 浏览器开启了 HSTS HTTP Strict Transport Security 功能 它会告诉浏览器只能通过 https 访问 绝对禁止
  • Dagger2的使用以及原理分析

    使用 Dagger2的使用说起来并不难 关键在于要掌握Dagger2的提供的几个注解及其意思 环境搭建 在模块级的build gradle文件中加入如下依赖 plugins id com android application id org