Dagger找不到其他注解处理器生成的类

14

我已经写了一个简单的注解处理器(只是为了好玩),它将收集Activity类上的注解,并生成一些样板代码,这些代码在我的以前的项目中已经写过。实际上,它会生成一个类似于以下模块的模块:

@Module
abstract class ActivityInjectorModule {
  @ContributesAndroidInjector
  abstract fun providesMain2Activity(): Main2Activity

  @ContributesAndroidInjector
  abstract fun providesMainActivity(): MainActivity
}

然而,当我使用dagger运行它时,dagger似乎无法找到我的注解处理器生成的类。虽然该类已经生成并存在于生成的目录中,我可以在我的源代码中使用它,但在编译时,dagger会产生以下异常。有什么专家建议吗?

error: cannot find symbol
@dagger.Component(modules = {dagger.android.AndroidInjectionModule.class, com.mallaudin.daggietest.di.AppModule.class, ActivityInjectorModule.class})
                                                                                                                       ^
  symbol: class ActivityInjectorModule

这是主应用程序组件。

@Singleton
@Component(
    modules = [
        AndroidInjectionModule::class,
        AppModule::class,
        ActivityInjectorModule::class
    ]
)
interface AppComponent : AndroidInjector<App> {


    @Component.Builder
    interface Builder {

        fun addContext(@BindsInstance ctx: Context): Builder

        fun build(): AppComponent
    }
}

ActivityInjectorModule类是由注解处理器生成的,存在于生成目录中。

应用程序类

class App : DaggerApplication() {
    override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
        return DaggerAppComponent.builder().addContext(this).build()
    }
}

如果我自己创建生成的类,一切都完美运作。但是在编译时,Dagger无法找到由我的注解处理器生成的类。{{}}
Yuriy Kulikov回答后,{{}}

yurily's answer

您可以看到生成的文件与同一包中的完全限定名称引用。尽管如此,dagger仍然报告错误。

如果有人想要尝试,这里是指向github存储库的链接


请问您能分享一下您的应用程序类或者您定义所有模块的位置吗? - Shweta Chauhan
1
@ShwetaChauhan 我已经更新了代码,请看一下。 - mallaudin
App类继承DaggerApplication(),对吧? - Shweta Chauhan
1
我已经在做这件事了。实际上,我已经对这个问题进行了很多搜索。我认为有一些棘手的部分,因为我不确定注解处理器的顺序,可能是dagger在编译之前做了一些事情。 - mallaudin
这是因为可能您的代码在Dagger编译之后才编译。为此,您应该确保相反的情况发生。 - coroutineDispatcher
即使我第一次这样做是有效的,但对于下一轮来说,注解处理器的顺序并不保证。 - mallaudin
3个回答

11

解决方案:

  1. 生成Java代码。 Kapt 不支持多轮处理
  2. 在尽可能早的轮次上编写生成的文件。

说明:

Javac注解处理器使用轮次而不是定义处理器顺序。因此,通常的简化算法如下:

  1. 收集所有Java源代码
  2. 运行所有注解处理器。任何注解处理器都可以使用Filer生成新文件。
  3. 收集所有生成的文件,如果有任何文件,则再次运行步骤2。
  4. 如果没有生成文件,则运行一轮RoundEnvironment.processingOver()返回true,表示这是最后一轮。

这里有一个非常好的过程解释

现在讲一下kaptKapt 使用javac 运行注释处理器。为了实现这一点,它首先运行kotlin编译器来生成java存根文件,然后在其上运行javac。目前kapt不支持多轮处理, 这意味着它不会为由注释处理器生成的kotlin类生成java存根。 注意:javac仍然使用多轮处理,只是不能捕获生成的kotlin源代码。 所以,回到你的问题。一种可能的选择是将生成的类移到一个单独的模块中,就像这里描述的那样
但最简单的方法是直接生成java代码,你的生成的java类将被javac自动捕获,启动第二轮注释处理,其中dagger将处理它们。
还有几个要注意的地方:
  • 不要在 RoundEnvironment.processingOver() == true 时生成代码,这不会触发另一轮。应该在看到注解的同一轮中生成代码。
  • 为了让注解处理器可见生成的代码,使用 Filer 进行编写。

哇!它与Java完美地配合。而且最后的注释非常有帮助。 - mallaudin

5

新答案 我不知道你正在使用kapt。如果你在build.gradle中添加以下内容,kapt可以处理你的类,即使没有完全限定名称(这是非常了不起的):

kapt {
    arguments {
        arg("argumentIncremental", 'true')
    }

    correctErrorTypes = true

}

更多关于此的信息:https://kotlinlang.org/docs/reference/kapt.html#non-existent-type-correction
如果有人在gradle中使用annotationProcessor(apt)遇到了相同的问题,之前的答案可能会有用。 简短回答:对于ActivityInjectorModule,请使用完全限定名称:
@dagger.Component(modules = {dagger.android.AndroidInjectionModule.class, com.mallaudin.daggietest.di.AppModule.class, com.mallaudin.daggietest.di.ActivityInjectorModule.class})

或者将两个文件放在同一个包中。

长答案:Dagger是一个注解处理器,它在您的代码编译之前(可能)运行,并且在其他注解处理器运行之前运行。处理器运行的顺序未定义。

Dagger注解处理器将处理使用@dagger.Component进行注释的TypeElement,并尝试查找包括“ActivityInjectorModule.class”在内的所有模块。问题在于,ActivityInjectorModule可能还没有被生成。因此,在这一点上,“ActivityInjectorModule”将没有包。 Dagger将假定ActivityInjectorModule与Component类位于同一个包中,并且不会添加导入。通常的解决方法是对生成的类使用完全限定名称,如果它们被其他注解处理器使用。有时,将注解处理移动到另一个gradle模块中是有意义的,但我认为这不是您想要的。


有趣的是,它也不能使用完全限定名称。我也尝试过了。当我观察Gradle生成的代码时,我意识到了这一点,并将文件放在相同的包中。 - mallaudin
我已经更新了问题并附上了截图,请查看。 - mallaudin
我已经添加了指向 Github 仓库的链接。 - mallaudin
1
嘿,谢谢你提供的链接。我意识到你正在使用kapt。请查看更新后的答案(它有效)。 - Yuriy Kulikov
在这种情况下,我只能想到将 argumentIncremental 切换为 false。您也可以尝试使用 Koin :-) - Yuriy Kulikov
显示剩余3条评论

0

解决这个问题可能有更优雅的方法,但最简单和可靠的解决方案是使用 javac 进行两次编译 —— 一次仅运行您的注释处理器,另一次执行所有正常操作。

javac 文档 指定了两个选项,应该会帮到您。

-proc: {none,only}

控制是否进行注释处理或编译。-proc:none 表示在没有注释处理的情况下进行编译。-proc:only 表示仅进行注释处理,没有后续编译。

-processor class1[,class2,class3...]

要运行的注释处理器的名称。这将绕过默认的发现过程。

第一次编译(仅运行自己的注释处理器)是

javac -proc:only -processor com.foo.bar.MyProcessor MyProject/src/*

而第二次通过(常规构建)是

javac MyProject/src/*

如果您正在使用类似Ant或Maven的工具,只需进行少量努力即可更新构建说明以进行两个编译器通道。 编辑:这是我尝试的Gradle说明 我没有使用Gradle的经验,但似乎您需要执行以下操作。
在Gradle构建脚本中,您需要定义预处理任务并将依赖项添加到javaCompile任务。
javaCompile.dependsOn myAnnotationTask

task myAnnotationTask(type: JavaCompile) {
    options.compilerArgs << '-proc:only' << '-processors com.foo.bar.MyAnnotationProcessor'
}

我已经包含了我最好的Gradle指令尝试。 - Matthew Pope

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接