IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android APT从入门到实战 -> 正文阅读

[移动开发]Android APT从入门到实战

  • APT是什么?有什么用?

APT(Annotation Processing Tool)即注解处理器,在编译的时候可以处理注解然后搞一些事情,也可以在编译时生成一些文件之类的。ButterKnife和EventBus都使用了APT技术,如果不会APT技术就很难看懂这两个框架的源码。

tempImage1629265217303.jpeg

  • 实现效果

我们来实现一个简单的功能,只要在任何类的成员变量上添加一个 @Print注解,就可以动态生成一个方法,然后把成员变量的变量名输出:

10.png

动态生成的类大概长这样:

11.png

  • 整理思路

  1. 首先我们需要创建两个JavaLibrary
  2. 一个用来定义注解,一个用来扫描注解
  3. 获取到添加注解的成员变量名
  4. 动态生成类和方法用IO生成文件
  • 实战

  • 创建一个空项目

1.png

  • 创建两个JavaLibrary

  1. 注解的Lib: apt-annotation
  2. 扫描注解的Lib: apt-processor

2.png

3.png

  • 创建完之后

4.png

  • app模块依赖两个Library

implementation project(path: ':apt-annotation')
annotationProcessor project(path: ':apt-processor')

8.png

  • 注解Lib中创建一个注解类

如果还不会自定义注解的同学,可以先去看我之前写的一篇Java自定义注解入门到实战

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface Print {

}

12.png

  • 扫描注解的Lib添加依赖

dependencies {
    //自动注册,动态生成 META-INF/...文件
    implementation 'com.google.auto.service:auto-service:1.0-rc6'
    annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
    //依赖apt-annotation
    implementation project(path: ':apt-annotation')
}

6.png

  • 创建扫描注解的类

7.png

  • 重写init方法,输出Hello,APT

注意: 这里是JavaLib,所以不能使用Log打印,这里可以使用Java的println()或注解处理器给我们提供的方法,建议使用注解处理器给我们提供的

13.png

  • 见证奇迹

现在我们已经完成了APT的基本配置,现在我们可以build一下项目了,成败在此一举

14.png

  • 踩坑指南

  1. 如果你已经成功输出了文本,说明APT已经配置好,可以继续下一步了
  2. 如果你失败了:
  1. 如果继承的时候找不到AbstractProcessor类,那你 创建的肯定不是JavaLibrary,你可以删掉重新创建
  2. 如果点击编译没反应,你可以试试先 clear一下项目再重新编译
  3. 如果都不行,就去检查一下前面流程的 依赖是否都配置正确
  • 继续完成功能

现在我们可以继续完成上面要实现的功能了,我们需要先来实现几个方法

/**
 * 要扫描扫描的注解,可以添加多个
 */
@Override
public Set<String> getSupportedAnnotationTypes() {
    HashSet<String> hashSet = new HashSet<>();
    hashSet.add(Print.class.getCanonicalName());
    return hashSet;
}

/**
 * 编译版本,固定写法就可以
 */
@Override
public SourceVersion getSupportedSourceVersion() {
    return processingEnv.getSourceVersion();
}

15.png

  • 定义注解

我们先在MianActivity中添加两个成员变量并使用我们定义的注解

17.png

  • 定义注解

真正解析注解的地方是在process方法,我们先试试能不能拿到被注解的变量名

/**
 * 扫描注解回调
 */
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    //拿到所有添加Print注解的成员变量
    Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Print.class);
    for (Element element : elements) {
        //拿到成员变量名
        Name simpleName = element.getSimpleName();
        //输出成员变量名
        processingEnv.getMessager().printMessage(Diagnostic.Kind.NOTE,simpleName);
    }
    return false;
}

18.png

  • 编译试一下

19.png

  • 生成类

既然能拿到被注解的变量名,后面就简单了,我们只需要用字符串拼出来一个工具类,然后用IO流写到本地就ok了

20.png

  • 查看效果

现在点击一下编译,然后我们可以看到app模块下的build文件已经有我们生成的类了

21.png

  • 调用方法

现在我们回到MainActivity,就可以直接调用这个动态生成的类了

9.png

tempImage1629273861493.gif

  • 实战结束

结束了吗…好像是结束了,但是上面拼接类的方法感觉一不小心就会写错,有没有更好的方法呢,我们先来看看EventBus的源码是怎么生成的:

23.png
看到大佬也是这样拼接的,这我就放心了🤡,我们再看一下ButterKnife的源码是怎么生成的:

24.png
ButterKnife的源码竟然不是用字符串拼接的!!! 隐约看到TypeSpec.classBuilder,这是啥玩意?不过身为资深的程序猿这点问题我们还是可以很容易的找到答案的

25.png

  • JavaPoet

经过一个小时的百度,大概研究了一下JavaPoet,这玩意好像可以帮我们以面向对象的思维来生成类,这样我们就不用手动拼接字符串的方式来生成类了,那我们来优化一下上面的代码:

先添加依赖
implementation 'com.squareup:javapoet:1.13.0'

29.png

/**
 * 扫描注解回调
 */
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
    //拿到所有添加Print注解的成员变量
    Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Print.class);

    //生成类
    TypeSpec.Builder classBuilder = TypeSpec
            .classBuilder("PrintUtil")
            .addModifiers(Modifier.PUBLIC, Modifier.FINAL);

    for (Element element : elements) {
        //拿到成员变量名
        Name simpleName = element.getSimpleName();
        //生成方法
        MethodSpec method = MethodSpec.methodBuilder("print$$"+simpleName)
                .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
                .returns(void.class)
                .addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
                .build();
        classBuilder.addMethod(method);
    }
    //包
    JavaFile javaFile = JavaFile
            .builder("com.lkx.helloapt", classBuilder.build())
            .build();
    try {
        javaFile.writeTo(processingEnv.getFiler());
    } catch (IOException e) {
        e.printStackTrace();
    }
    return false;
}

27.png

  • 编译一下

28.png

tempImage1629276608514.jpeg

  • 总结

  1. APT可以在编译器扫描注解帮我们提前生成类
  2. JavaPoet可以帮我们优雅的生成类,再也不用拼接了
  3. APT最主要的功能就是可以替代反射的一些功能,避免降低性能
  4. APT只会在编译时影响一点点速度,在运行期不会,而反射刚好相反
  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-19 12:09:56  更:2021-08-19 12:10:56 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 10:18:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码