Android Studio中的多模块注释处理

Ada*_*abb 6 java android annotations annotation-processing

我在Android Studio中有一个包含多个模块的项目.模块可能依赖于另一个模块,例如:

模块PhoneApp - >模块FeatureOne - >模块服务

我已经在根模块中包含了我的注释处理,但是android-apt注释处理仅在最顶层(PhoneApp)发生,因此理论上它应该在编译时可以访问所有模块.但是,我在生成的java文件中看到的只是PhoneApp中注释的类,而其他模块中没有注释.

PhoneApp/build/generated/source/apt/debug/.../GeneratedClass.java
Run Code Online (Sandbox Code Playgroud)

在其他模块中,我在中间目录中找到一个生成的文件,该文件仅包含该模块中带注释的文件.

FeatureOne/build/intermediates/classes/debug/.../GeneratedClass.class
FeatureOne/build/intermediates/classes/debug/.../GeneratedClass.java
Run Code Online (Sandbox Code Playgroud)

我的目标是在PhoneApp中创建一个生成的文件,允许我从所有模块访问带注释的文件.不完全确定为什么每个代码生成过程都在运行,并且无法在PhoneApp上聚合所有注释.任何帮助赞赏.

到目前为止,代码相当简单直接,checkIsValid()省略,因为它正常工作:

注释处理器:

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    try {

        for (Element annotatedElement : roundEnv.getElementsAnnotatedWith(GuiceModule.class)) {
            if (checkIsValid(annotatedElement)) {
                AnnotatedClass annotatedClass = new AnnotatedClass((TypeElement) annotatedElement);
                if (!annotatedClasses.containsKey(annotatedClass.getSimpleTypeName())) {
                    annotatedClasses.put(annotatedClass.getSimpleTypeName(), annotatedClass);
                }
            }
        }

        if (roundEnv.processingOver()) {
            generateCode();
        }

    } catch (ProcessingException e) {
        error(e.getElement(), e.getMessage());
    } catch (IOException e) {
        error(null, e.getMessage());
    }

    return true;
}

private void generateCode() throws IOException {
    PackageElement packageElement = elementUtils.getPackageElement(getClass().getPackage().getName());
    String packageName = packageElement.isUnnamed() ? null : packageElement.getQualifiedName().toString();

    ClassName moduleClass = ClassName.get("com.google.inject", "Module");
    ClassName contextClass = ClassName.get("android.content", "Context");
    TypeName arrayOfModules = ArrayTypeName.of(moduleClass);

    MethodSpec.Builder methodBuilder = MethodSpec.methodBuilder("juice")
            .addParameter(contextClass, "context")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .returns(arrayOfModules);

    methodBuilder.addStatement("$T<$T> collection = new $T<>()", List.class, moduleClass, ArrayList.class);

    for (String key : annotatedClasses.keySet()) {

        AnnotatedClass annotatedClass = annotatedClasses.get(key);
        ClassName className = ClassName.get(annotatedClass.getElement().getEnclosingElement().toString(),
                annotatedClass.getElement().getSimpleName().toString());

        if (annotatedClass.isContextRequired()) {
            methodBuilder.addStatement("collection.add(new $T(context))", className);
        } else {
            methodBuilder.addStatement("collection.add(new $T())", className);
        }

    }

    methodBuilder.addStatement("return collection.toArray(new $T[collection.size()])", moduleClass);

    TypeSpec classTypeSpec = TypeSpec.classBuilder("FreshlySqueezed")
            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
            .addMethod(methodBuilder.build())
            .build();

    JavaFile.builder(packageName, classTypeSpec)
            .build()
            .writeTo(filer);
}
Run Code Online (Sandbox Code Playgroud)

这只是一个与Guice一起使用的注释处理演示,如果有人好奇的话.

那么如何才能将所有带注释的类包含在所有模块生成的PhoneApp .java文件中?

Den*_*mus 2

回答有关 SO 的问题永远不会太晚,所以......

\n\n

我在一项工作任务中遇到了非常类似的并发症。

\n\n

我能够解决它。

\n\n

简洁版本

\n\n

关于 moduleB 中 moduleA 生成的类,您需要了解的只是包和类名。可以将其存储在放置在已知包中的某种MyClassesRegistrar生成的类中。使用后缀避免名称冲突,通过包获取注册商。实例化它们并使用它们中的数据。

\n\n

伦敦版

\n\n

首先 - 你将无法仅在最顶层模块包含仅编译时依赖项(让我们将其称为“app”模块,就像典型的 android 项目结构一样)。注释处理不能那样工作,而且据我所知 -对此无能为力。

\n\n

现在详细介绍。我的任务是这样的:\n我有人工编写的带注释的类。我将它们命名为“事件”。在编译时,我需要为这些事件生成帮助程序类,以合并其结构和内容(静态可用(注释值、常量等)和运行时可用(在使用后者时,我将事件对象传递给这些帮助程序)。类名取决于带有后缀的事件类名,因此在代码生成完成之前我不知道它。

\n\n

因此,生成助手后,我创建一个工厂并生成代码以根据MyEvent.class提供的提供新的助手实例。问题是:我只需要应用程序模块中的一个工厂,但它应该能够为库模块中的事件提供帮助程序 - 这不能简单地完成。

\n\n

我所做的是:

\n\n
    \n
  1. 跳过为我的应用程序模块依赖的模块生成工厂;

  2. \n
  3. 在非应用程序模块中生成所谓的 HelpersRegistrar 实现:

    \n\n

    \xe2\x80\x93 它们都共享同一个包(稍后你就会知道为什么);

    \n\n

    \xe2\x80\x93 他们的名字不会因为后缀而冲突(见下文);

    \n\n

    \xe2\x80\x93 应用程序模块和库模块之间的区别是通过 javac"-Amylib.suffix=MyModuleName"参数完成的,用户必须设置 - 这是一个限制,但很小。app模块不能指定后缀;

    \n\n

    \xe2\x80\x93 HelpersRegistrar 生成的实现可以提供我将来生成工厂代码所需的所有内容:事件类名称、帮助程序类名称、包(这两个共享包用于帮助程序和事件之间的包可见性)-所有字符串,合并在 POJO 中;

  4. \n
  5. 在应用程序模块中,我生成助手 - 像往常一样,然后我通过它们的包获取 HelperRegistrars,实例化它们,运行它们的内容,以使用从其他模块提供助手的代码来丰富我的工厂。我所需要的只是类名和包。

  6. \n
  7. 瞧\xc3\xa0!我的工厂可以提供来自应用程序模块和其他模块的助手实例。

  8. \n
\n\n

剩下的唯一不确定性是在应用程序模块和其他模块中创建和运行处理器类实例的顺序。我还没有找到任何关于此的可靠信息,但运行我的示例表明编译器(因此,代码生成)首先在我们依赖的模块中运行,然后在应用程序模块中运行(否则应用程序模块的编译将为 f. .cked)。这使我们有理由期望不同模块中代码处理器执行的已知顺序。

\n\n

另一种稍微相似的方法是:跳过注册器,在所有模块中生成工厂并在应用程序模块中编写工厂以使用其他工厂,您以与上面的注册器相同的方式获取和命名工厂。

\n\n

示例可以在这里看到:https://github.com/techery/janet-analytics - 这是一个我应用了这种方法的库(因为我有工厂,所以没有注册商,但对你来说情况可能并非如此)。

\n\n

PS:后缀参数可以切换为更简单的“-Amylibraryname.library=true”,工厂/注册商名称可以自动生成/递增

\n