不是大牛,也能上手组件化(二)

这是我参与更文挑战的第8天,活动详情查看: 更文挑战

前情提要

上一篇文章我们学习了通过 gradle 脚本控制 app 的打包生成逻辑,算是可以完成了组件化的第一步。在独立打包过程中,这样就够了,但是如果在集成打包的时候我们需要从app 工程跳转到 userinfo 工程,需要怎么处理呢?

如何跳转到子模块

当然这时候我们可以直接通过 intent 的方式来实现,但是如果这样直接写上的话。在独立打包的时候,这些类是找不到的。这时候就需要我们来处理了。

// 直接跳转的方式,在独立打包的时候 UserInfoActivity.class 是找不到的
Intent intent = new Intent(MainActivity.this, UserInfoActivity.class);
startActivity(intent);
复制代码

我们可以想到的有 eventbus、广播、类加载等方式,我们来写一个反射的示例代码。

private void goUserInfo() {
    // 使用类加载器的方式
    try {
        Class<?> clazz = Class.forName("xyz.xyz0z0.userinfo.UserInfoActivity");
        Intent intent = new Intent(MainActivity.this, clazz);
        startActivity(intent);
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }
}
复制代码

因为这里 intent 的第二个参数接收的是一个 class 对象,所以我们通过 Class.forName 构造出我们需要跳转的类就可以这样使用了。我们运行尝试一下,是可以正常跳转的。

这里我们更进一步,把这些需要跳转的类路径全部通过一个 map 保存起来,在需要跳转的时候直接获取就可以了,这种思想也就是 ARouter 的思想。但是如果一个个的手动添加不仅繁琐而且容易出错,自然是需要采用程序来自动添加,而且是需要在打包之前就需要添加上的,那么我们能够采用什么技术呢?就是 APT ,编译时注解技术,在编译时替我们自动生成代码,像 eventbus、butterknife 等框架都采用了 APT 相关技术。

APT 技术实战

那么应该如何实现呢,传统方式是在编译过程中通过我们的注解获取到相关类信息,然后手动拼接代码并生成成文件。当然这也很繁琐,自然可以通过框架来解决,需要的框架就是 JakeWharton 大声开发的 javapoet (github.com/square/java…

首先我们模仿其他开源项目新建 annotation 和 compiler 两个 java 项目。(我这里为了后续的仿写 ARouter 框架,就将这两个子项目分别命名为 arouter-annotations 和 arouter-compiler)。在 annotation 项目中新建上我们需要的注解类,在 compiler 中新建我们需要的注解处理类 ARouterProcessor.java 。

// google 的 auto-service 提供的,让我们的注解处理类可以成为一个服务,在编译过程中进行一些处理
@AutoService(Processor.class)
// 这个注解表示我们需要关注哪一个注解
@SupportedAnnotationTypes({"xyz.xyz0z0.arouter_annotations.ARouter"})
// 支持的版本号
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class ARouterProcessor extends AbstractProcessor {
    // 日志相关
    private Messager messager;
    // 生成文件相关操作
    private Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnv) {
        super.init(processingEnv);
        messager = processingEnv.getMessager();
        filer = processingEnv.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>> start");
        if (annotations.isEmpty()) {
            return false;
        }
        // 这里可以获取处理过程中拿到的注解详细
        for (TypeElement annotation : annotations) {
            messager.printMessage(Diagnostic.Kind.NOTE, "annotation " + annotation.toString());
        }
        messager.printMessage(Diagnostic.Kind.NOTE, ">>>>> end");
        return false;
    }
}
复制代码

接着我们的 app 项目分别依赖 annotations 和 compile 项目,因为我们的 ARouterProcessor 设置了关注 “xyz.xyz0z0.arouter_annotations.ARouter” 这个注解,所以我们需要在 app 项目中使用一下这个注解。接着编译我们的项目,就可以在 build 面板中看到我们的注解处理器输出的信息了。具体代码可以到 github 上面查看。

JavaPoet 上手

到这里,我们已经了解了 APT 技术和流程了,那么我们来试试生成代码吧,这里就需要用到 JavaPoet 了。JavaPoet 采用了面向对象的方法,我们通过框架为我们提供的方法可以分别构造出方法,类,包,最后生成我们的需要的文件,就是这么简单。下面我们试试输出一个简单的 HelloWorld 文件吧。

直接上代码。

private void generateHelloWorld() {
    // 方法
    MethodSpec mainMethod = MethodSpec.methodBuilder("main")
            .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
            .returns(void.class)
            .addParameter(String[].class, "args")
            .addStatement("$T.out.println($S)", System.class, "Hello world.")
            .build();
    // 类
    TypeSpec typeSpec = TypeSpec.classBuilder("MainTest")
            .addMethod(mainMethod)
            .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
            .build();
    // 包
    JavaFile javaFile = JavaFile.builder("com.hello.test", typeSpec)
            .build();
    // 生成文件
    try {
        javaFile.writeTo(filer);
    } catch (IOException e) {
        e.printStackTrace();
        messager.printMessage(Diagnostic.Kind.NOTE, "生成文件失败,异常:" + e.getMessage());
    }
}
复制代码

再次编译,查看一下 app 项目里面的 build/generated/ap_generated_sources 目录,就可以找到我们生成的 MainTest 文件了。这里需要解释一下,因为我们的注解是在 app 项目中使用的,所以生成的代码也是在该项目下面。

image-20210620115538431

这样我们就初步完成了 JavaPoet 的使用了,也就能理解 butterknife 这一类采用了编译时注解技术的框架的实现原理。看来我们和 JakeWharton 大神的距离又缩短了一步了。这篇分享就到这里,下一期让我们自定义实现我们的路由跳转,我是不要注水,我们下期见。

本文是个人的学习记录,不免对技术点有错误或不够深入的理解,还望大神小白批评指教。
对应的源码地址 github.com/xyz0z0/Comp… 可以参考对应的 commit 记录查看

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享