Dagger2的使用以及原理分析

使用

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的简单使用

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值