如何通过反射获取Kotlin属性getter上的Java注解?
问题根源
@ChecksSdkIntAtLeast的注解保留策略是CLASS级别,这意味着它只在编译阶段有效,编译成字节码后,注解信息不会被保留到运行时环境中。而Java和Kotlin的反射机制只能读取RUNTIME级别的注解,所以你用常规反射肯定拿不到这个注解。
可行的验证方案
1. 编译期注解处理器检查
写一个简单的注解处理器,在编译阶段直接扫描代码里的@ChecksSdkIntAtLeast注解,验证参数是否正确,甚至检查它是否存在。一旦发现问题,直接在编译时报错,从源头杜绝遗漏或参数错误的情况。
大致实现步骤:
- 继承
AbstractProcessor,重写process方法 - 通过
elementUtils.getElementsAnnotatedWith(ChecksSdkIntAtLeast.class)拿到所有带该注解的元素 - 对每个元素(比如DeviceApi里的getter方法),获取注解的
value参数,判断是否等于23 - 如果不符合要求,用
processingEnv.getMessager().printError()抛出编译错误,阻止构建
2. 字节码分析工具读取CLASS注解
用ASM、ByteBuddy这类能直接操作字节码的库,在测试时加载DeviceApi的.class文件,解析其中的注解信息。这类工具不受运行时反射的限制,可以直接读取CLASS级别的注解。
举个ASM的简单示例:
ClassReader classReader = new ClassReader("com.your.package.DeviceApi"); classReader.accept(new ClassVisitor(Opcodes.ASM9) { @Override public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); // 匹配Kotlin属性生成的getter方法名 if ("getIsApi23AndAbove".equals(name)) { return new MethodVisitor(Opcodes.ASM9, mv) { @Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { // 检查注解的全类名描述符 if ("Landroidx/annotation/ChecksSdkIntAtLeast;".equals(desc)) { return new AnnotationVisitor(Opcodes.ASM9) { @Override public void visit(String attrName, Object value) { if ("value".equals(attrName) && !23.equals(value)) { throw new AssertionError("ChecksSdkIntAtLeast注解参数错误,预期23"); } } }; } return super.visitAnnotation(desc, visible); } }; } return mv; } }, ClassReader.SKIP_DEBUG);
3. 间接验证:检查编译警告是否消除
@ChecksSdkIntAtLeast的核心作用是告诉编译器,这个属性的返回值可以作为SDK版本判断依据,从而消除调用高API方法时的编译警告。你可以写一段测试代码,在里面调用需要API23的方法(比如NotificationChannel的初始化),并用isApi23AndAbove作为判断条件,然后验证编译时是否没有出现"Call requires API level 23"的警告。
如果要自动化这个检查,可以自定义Lint规则:扫描所有使用isApi23AndAbove作为条件分支的代码,验证内部调用的高API方法版本是否和注解声明的一致,同时检查isApi23AndAbove本身是否带有正确的注解。
内容的提问来源于stack exchange,提问作者StormFoo
相关产品推荐
相关产品推荐

