8.Dagger2.38.1源码解析-Demo梳理component节点生成代码

本文详细解析Dagger2组件(component)的代码生成逻辑,包括类名生成、类继承、Creator内部类创建以及component节点间的依赖关系。通过对ShardImplementation的generate方法的分析,揭示了DaggerComponentProcessor_ProcessorComponent类的生成过程,包括factory方法、接口方法的实现。同时,文章探讨了如何处理复杂的依赖关系,生成相应的代码块。

前言

考虑到这块代码的复杂性,我们以当前ProcessorComponent节点生成DaggerComponentProcessor_ProcessorComponent作为demo案例讲解。

在讲解前,我们必须先把路由图整理出来,该路由图的作用是依赖的关联生成对应的变量和代码块:

component代码实现核心关系图文件夹下,灰常灰常重要

  • 在熟悉下面demo前务必认真、多次、对照前面的关系图去理解当前路由图。

component代码生成器是ComponentGenerator类,该类的topLevelTypes执行componentImplementation.generate()完成代码生成逻辑,如下:

public TypeSpec generate() {
    return componentShard.generate();
}

如上代码所示,这里的componentShared(ShardImplementation类)表示当前正在处理的component碎片,也可以理解为每一个再处理的component节点会有一个ShardImplementation类作为碎片处理器。

下面对照ShardImplementation的generate理解component代码逻辑的生成.

ShardImplementation的generate完成component代码逻辑的生成

生成了DaggerComponentProcessor_ProcessorComponent类,讲解过程中细节方面自行对照该类进行理解。

一 生成类名

**目标实现:**生成了DaggerComponentProcessor_ProcessorComponent作为类名。

代码逻辑:

  1. classBuilder(name),创建一个name的类;

  2. name参数是通过ShardImplementation构造函数传递过来的参数;

  3. 在ComponentImplementation构造函数中新建ShardImplementation时,componentNames.get(graph.componentPath())就是name参数的值;

      @Inject
         ComponentImplementation(...){
     		...
     		this.componentShard = new ShardImplementation(componentNames.get(graph.componentPath()));
     		...
     }
    
  4. ComponentNames.get方法获取生成的component节点生成的类名,这里我们知道使用的是rootName

     ClassName get(ComponentPath componentPath) {
         return componentPath.atRoot()
                 ? rootName
                 //父节点生成的名称.currentComponent + "Impl"
                 : rootName.nestedClass(namesByPath.get(componentPath) + "Impl");
     }
    
  5. rootName在ComponentNames构造函数中实现:

    @Inject
    ComponentNames(
    @TopLevel BindingGraph graph,
    KeyFactory keyFactory
    ) {
    this.rootName = getRootComponentClassName(graph.componentDescriptor());

    }

  6. 如下代码可以看到生成component节点的类规则:

     public static ClassName getRootComponentClassName(ComponentDescriptor componentDescriptor) {
         checkState(!componentDescriptor.isSubcomponent());
         ClassName componentName = ClassName.get(componentDescriptor.typeElement());
         return ClassName.get(componentName.packageName(), "Dagger" + classFileName(componentName));
     }
    
  • 生成的类在当前component节点同一个包下,并且类名是 “Dagger” + 当前component节点父类一级级的以"_"作为分隔符拼接到component节点,例如DaggerComponentProcessor_ProcessorComponent

##二 当前生成的Component类继承component节点 ##

目标实现:
当前DaggerComponentProcessor_ProcessorComponent继承ProcessorComponent节点:

DaggerComponentProcessor_ProcessorComponent implements ComponentProcessor.ProcessorComponent

代码逻辑

TypeSpecs.addSupertype(builder, graph.componentTypeElement());

##三 生成Creator内部类 ##

目标实现:

生成一个Factory类继承ComponentProcessor.ProcessorComponent.Factory

  private static final class Factory implements ComponentProcessor.ProcessorComponent.Factory {
    @Override
    public ComponentProcessor.ProcessorComponent create(XProcessingEnv xProcessingEnv,
        ImmutableSet<BindingGraphPlugin> externalPlugins) {
      Preconditions.checkNotNull(xProcessingEnv);
      Preconditions.checkNotNull(externalPlugins);
      return new DaggerComponentProcessor_ProcessorComponent(xProcessingEnv, externalPlugins);
    }
  }

代码逻辑:

  1. 调用addCreator()方法;

  2. 调用ComponentCreatorImplementationFactory.create用于生成creator内部类,并且存储于ComponentCreatorImplementation对象。这里调用的是BuilderForCreatorDescriptor对象的build方法:

  • (0)生成的creator类名:

  • ① componentImplementation.getCreatorName():调用getCreatorName方法获取;

  • ② componentNames.getCreatorName(graph.componentPath())调用getCreatorName方法获取creator类名,如下所示表示生成当前DaggerComponentProcessor_ProcessorComponent内部类

     	rootName.nestedClass(creatorNamesByPath.get(componentPath))
    
  • ③ creatorNamesByPath变量在ComponentNames类构造函数中定义,调用creatorNamesByPath方法,该方法作用:实现Factory模式下使用Factory作为类名,Builder模式下使用Builder作为类名,所以当前生成的类名为:Factory

  • (1)setModifiers():修饰符private static final

  • (2)setSupertype():当前creator节点继承ComponentProcessor.ProcessorComponent.Factory

  • (3)addConstructor():添加构造函数,如果当前不存在父级component,那么使用默认构造函数,这里不另外生成;

  • (4)this.fields = addFields():添加变量,这里针对的是Builder模式下的setterMethod方法的参数,当前是Factory模式,所以该变量为空;

  • (5)addSetterMethods():Builder模式下的setterMethod方法在Creator类中创建新的方法,当前是Factory模式,所以这里没有实现;

  • (6) addFactoryMethod():Factory模式下的方法或Builder模式下的buildMethod方法用于业务逻辑实现。这里针对Factory做如下操作:

  • ① factoryMethodBuilder():重写create方法,方法类型是ComponentProcessor.ProcessorComponent

  • create修饰符和返回类型:public修饰,方法返回类型是ComponentProcessor.ProcessorComponent

  • ③ factoryMethodParameters():create方法参数,沿用ProcessorComponent.Factory的create方法参数

  • create方法添加代码块:

  • ④a:对参数加上notNull处理:

     	Preconditions.checkNotNull(xProcessingEnv);
         Preconditions.checkNotNull(externalPlugins);
    
  • ④b:创建DaggerComponentProcessor_ProcessorComponent对象,代码块:return new DaggerComponentProcessor_ProcessorComponent(xProcessingEnv, externalPlugins);

  • 以上生成ComponentCreatorImplementation对象:当前Factory类、Factory类名、变量(不存在变量)作为参数

  1. 将当前生成的ComponentCreatorImplementation对象存储在当前正在处理的ProcessorComponent对应的ComponentImplementation对象的属性的ShardImplementation对象的typeSpecsMap集合中,TypeSpecKind类型使用COMPONENT_CREATOR表示。

##四 生成factoryMethod方法 ##

该方法的作用是实例化Factory对象。

**目标实现:**生成factory()方法

  public static ComponentProcessor.ProcessorComponent.Factory factory() {
    return new Factory();
  }

业务逻辑:

  1. 判断当前是否是subcomponent节点,因为不是所以调用createRootComponentFactoryMethod方法;

  2. 在当前ProcessorComponent存在Factory情况下,生成一个方法factory方法;

  • (1)先校验,当前ProcessorComponent不能存在 方法名是factory&&factory方法无参&&没有使用static修饰 ,否则报错。还好我们存在的ProcessorComponent中的factory方法使用了static修饰了,否则即报错。

  • (2)生成的方法名是factorypublic static修饰,ComponentProcessor.ProcessorComponent.Factory作为返回类型,代码块return new Factory()

##五 生成InterfaceMethod接口方法 ##

这里的逻辑极其复杂,一定要先认真查看上面的路由关系图,是当前实现component代码实现核心的关系图。

当前方法的作用是重写ProcessorComponent的入口方法,并且对当前入口方法实现,并且该入口方法实现过程中的依赖的一步步实现。

目标实现:

  1. 重写入口方法:DaggerComponentProcessor_ProcessorComponent重写了inject方法;

     @Override
     public void inject(ComponentProcessor processor) {
     	injectComponentProcessor(processor);
     }
    
  2. 入口方法的实现的代码块实现的方法:injectComponentProcessor(processor)代码块实现的injectComponentProcessor方法;

  • (1)injectComponentProcessor方法的实现

     @CanIgnoreReturnValue
     private ComponentProcessor injectComponentProcessor(ComponentProcessor instance) {
     	ComponentProcessor_MembersInjector.injectInjectBindingRegistry(instance, (InjectBindingRegistry) injectBindingRegistryImplProvider.get());
     	ComponentProcessor_MembersInjector.injectFactoryGenerator(instance, sourceFileGeneratorOfProvisionBinding());
     	ComponentProcessor_MembersInjector.injectMembersInjectorGenerator(instance, sourceFileGeneratorOfMembersInjectionBinding());
     	ComponentProcessor_MembersInjector.injectProcessingSteps(instance, immutableListOfXProcessingStep());
     	ComponentProce
<think>根据您的问题,您想知道在Android Studio 2024.3.1版本中,如何正确添加Hilt和ButterKnife的Gradle插件依赖。这两个插件依赖需要添加到项目根目录下的`build.gradle`(有时也叫做`build.gradle.kts`,如果是Kotlin脚本)文件中。 注意:在较新的Android Studio版本中,项目结构可能有两种构建脚本: 1. 项目根目录的`build.gradle`(或`build.gradle.kts`) - 用于配置整个项目的构建脚本和插件依赖。 2. 每个模块(如app模块)的`build.gradle`(或`build.gradle.kts`) - 用于配置特定模块的构建。 您提到的两个插件依赖: - `classpath 'com.google.dagger:hilt-android-gradle-plugin:2.41'` 是Hilt的Gradle插件。 - `classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.1'` 是ButterKnife的Gradle插件。 这些插件依赖应该添加到项目根目录的`build.gradle`文件中的`buildscript.dependencies`块内。 以下是具体的步骤和代码: ### 步骤1:打开项目根目录的build.gradle文件 通常,项目根目录的`build.gradle`文件位于项目的顶层目录,与`settings.gradle`文件同级。 ### 步骤2:在buildscript块中添加依赖 在`buildscript`块中,确保有`repositories`(如google()和mavenCentral())和`dependencies`块。然后在`dependencies`块中添加这两个插件的classpath。 ### 示例代码(Groovy DSL - 即使用.gradle文件) ```groovy // 项目根目录的build.gradle文件 buildscript { repositories { google() mavenCentral() // 如果有其他仓库也可以添加 } dependencies { // 其他classpath,例如Android Gradle插件 classpath 'com.android.tools.build:gradle:8.3.2' // 请使用与您项目匹配的版本 // 添加Hilt插件 classpath 'com.google.dagger:hilt-android-gradle-plugin:2.41' // 添加ButterKnife插件 classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.1' // 注意:如果您使用Kotlin,还应该添加Kotlin插件 classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22" // 请使用与您项目匹配的Kotlin版本 } } // 所有模块的配置,通常放在allprojects块中 allprojects { repositories { google() mavenCentral() } } ``` ### 注意事项: 1. **版本兼容性**:请确保您使用的插件版本与您的项目其他依赖(如Android Gradle插件版本、Kotlin版本)兼容。特别是Hilt版本2.41可能不是最新,建议查看官方文档获取最新版本。 2. **ButterKnife的使用**:请注意,ButterKnife已经不再维护,官方推荐使用ViewBinding或DataBinding。如果您的项目是新项目,建议不要使用ButterKnife。 3. **应用插件**:在模块的`build.gradle`中,您还需要应用这些插件。例如,在app模块的`build.gradle`中,您需要添加: ```groovy // 对于Hilt plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' id 'kotlin-kapt' id 'dagger.hilt.android.plugin' // 应用Hilt插件 } ``` 对于ButterKnife,如果您仍要使用,在模块的`build.gradle`中应用: ```groovy apply plugin: 'com.jakewharton.butterknife' ``` 或者使用plugins块(注意ButterKnife插件可能不支持新的plugins DSL,所以可能需要使用apply方式)。 4. **同步项目**:添加依赖后,同步项目(Sync Project with Gradle Files)。 ### 如果使用Kotlin DSL(build.gradle.kts) 如果您的项目使用Kotlin DSL(即`build.gradle.kts`文件),则添加方式如下: ```kotlin // 项目根目录的build.gradle.kts buildscript { repositories { google() mavenCentral() } dependencies { classpath("com.android.tools.build:gradle:8.3.2") // Android Gradle插件 classpath("com.google.dagger:hilt-android-gradle-plugin:2.41") // Hilt classpath("com.jakewharton:butterknife-gradle-plugin:10.2.1") // ButterKnife classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22") // Kotlin插件 } } allprojects { repositories { google() mavenCentral() } } ``` ### 最后 请根据您的项目实际情况调整版本号,特别是Android Gradle插件版本和Kotlin版本。您可以在[Android Gradle插件版本说明](https://developer.android.com/studio/releases/gradle-plugin)中查看推荐版本。 §§相关问题§§: 1. 如何在模块的build.gradle中应用Hilt和ButterKnife插件? 2. ButterKnife和ViewBinding有什么区别?为什么ButterKnife不再推荐使用? 3. Hilt依赖注入框架中,如何定义和注入一个依赖? 4. 如何解决Gradle同步时出现的插件版本冲突问题?</think>### 在 Android Studio 2024.3.1 中配置 Hilt 和 ButterKnife 在 Android Studio 2024.3.1 (Giraffe) 中,您需要将插件依赖添加到项目根目录的 `build.gradle` 文件中。以下是具体步骤: #### 1. 打开项目根目录的 `build.gradle` 文件 路径:`YourProject/build.gradle`(不是 app 模块的 build.gradle) #### 2. 添加依赖到 `buildscript.dependencies` 块 ```groovy // 项目根目录的 build.gradle buildscript { repositories { google() mavenCentral() } dependencies { // Android Gradle 插件(必须与你的 AS 版本兼容) classpath 'com.android.tools.build:gradle:8.3.2' // 2024.3.1 推荐版本 // Hilt 插件 classpath 'com.google.dagger:hilt-android-gradle-plugin:2.48' // 建议使用最新版 // ButterKnife 插件 classpath 'com.jakewharton:butterknife-gradle-plugin:10.2.1' // 如果你使用 Kotlin classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22" // 使用与项目兼容的版本 } } allprojects { repositories { google() mavenCentral() } } ``` #### 3. 在 app 模块中应用插件 在 app 模块的 `build.gradle` 中: ```groovy // app/build.gradle plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' // 如果使用 Kotlin id 'kotlin-kapt' // Kotlin 注解处理 id 'dagger.hilt.android.plugin' // Hilt 插件 } apply plugin: 'com.jakewharton.butterknife' // ButterKnife 插件 dependencies { // Hilt 依赖 implementation 'com.google.dagger:hilt-android:2.48' kapt 'com.google.dagger:hilt-android-compiler:2.48' // ButterKnife 依赖 implementation 'com.jakewharton:butterknife:10.2.1' annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.1' } ``` ### 版本兼容性说明 | 组件 | 推荐版本 | 说明 | |------|----------|------| | Android Gradle Plugin | 8.3.2 | 与 AS 2024.3.1 完全兼容 | | Hilt | 2.48 | 最新稳定版 | | ButterKnife | 10.2.1 | 最终维护版本 | | Kotlin | 1.9.22 | AS 2024.3.1 内置版本 | ### 重要注意事项 1. **同步 Gradle**:修改后点击 "Sync Now" 2. **JDK 要求**:确保使用 JDK 17(File > Project Structure > SDK Location) 3. **ButterKnife 替代**:考虑迁移到 View Binding 或 Jetpack Compose ```gradle // 替代 ButterKnife 的 View Binding android { buildFeatures { viewBinding true } } ``` 4. **Hilt 应用类**:确保 Application 类使用 `@HiltAndroidApp` ```java @HiltAndroidApp public class MyApplication extends Application { // ... } ``` ### 常见问题解决 如果遇到 "Plugin [id: 'dagger.hilt.android.plugin'] was not found": 1. 检查根 build.gradle 中的仓库配置(必须有 google() 和 mavenCentral()) 2. 确保网络可以访问 Maven 仓库 3. 尝试清理缓存:File > Invalidate Caches / Restart
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值