写点什么

字节三面:ButterKnife 为什么执行效率为什么比其他注入框架高?它的原理是什么

用户头像
Android架构
关注
发布于: 刚刚

com.rhythm7.lib_compiler.RandomProcessor


也就是说,你所声明的注解处理器都会在被写入这个配置文件中。 这样子,当外部程序装载这个模块的时候,就能通过该模块的 jar 包下的 META-INF/services 下找到具体的注解处理器的实现类名,并加载实例化,完成模块的注入。 注解处理器需要实现AbstractProcessor接口,并实现对应的方法


  • init() 可选 在该方法中可以获取到processingEnvironment对象,借由该对象可以获取到生成代码的文件对象, debug 输出对象,以及一些相关工具类

  • getSupportedSourceVersion() 返回所支持的 java 版本,一般返回当前所支持的最新 java 版本即可

  • getSupportedAnnotationTypes() 你所需要处理的所有注解,该方法的返回值会被process()方法所接收

  • process() 必须实现 扫描所有被注解的元素,并作处理,最后生成文件。该方法的返回值为 boolean 类型,若返回 true,则代表本次处理的注解已经都被处理,不希望下一个注解处理器继续处理,否则下一个注解处理器会继续处理。

初始化

较详细代码如下:


private static final List<Class<? extends Annotation>> RANDOM_TYPES= Arrays.asList(RandomInt.class, RandomString.class);


private Messager messager;private Types typesUtil;private Elements elementsUtil;private Filer filer;


private TypeonProcess()per.init(processingEnv);messager = processingEnv.getMessager();typesUtil = processingEnv.getTypeUtils();elementsUtil = processingEnv.getElementUtils();filer = processingEnv.getFiler();}


@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.latestSupported();}


@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> annotations = new LinkedHashSet<>();


for (Class<? extends Annotation> annotation : RANDOM_TYPES) {annotations.add(annotation.getCanonicalName());}return annotations;}

处理注解

process()方法中执行以下操作:


1.扫描所有注解元素,并对注解元素的类型做判断


for (Element element : roundEnv.getElementsAnnotatedWith(RandomInt.class)) {//AnnotatedRandomInt 是对被 RandomInt 注解的 Elment 的简单封装 AnnotatedRandomInt randomElement = new AnnotatedRandomInt(element);messager.printMessage(Diagnostic.Kind.NOTE, randomElement.toString());//判断被注解的类型是否符合要求 if (!element.asType().getKind().equals(TypeKind.INT)) {messager.printMessage(Diagnostic.Kind.ERROR, randomElement.getSimpleClassName().toString() + "#"


  • randomElement.getElementName().toString() + " is not in valid type int");}


//按被注解元素所在类的完整类名为 key 将被注解元素存储进 Map 中,后面会根据 key 生成类文件 String qualifier = randomElement.getQualifiedClassName().toString();if (annotatedElementMap.get(qualifier) == null) {annotatedElementMap.put(qualifier, new ArrayList<AnnotatedRandomElement>());}annotatedElementMap.get(qualifier).add(randomElement);}

生成类文件

将之前以注解所在类为 key 的 map 遍历,并以 key 值为分组生成类文件。


for (Map.Entry<String, List<AnnotatedRandomElement>> entry : annotatedElementMap.entrySet()) {MethodSpec constructor = createConstructor(entry.getValue());TypeSpec binder = createClass(getClassName(entry.getKey()), constructor);JavaFile javaFile = JavaFile.builder(getPackage(entry.getKey()), binder).build();javaFile.writeTo(filer);}


生成类、构造函数、代码段以及文件都是利用到了javapoet依赖库。当然你也可以选择拼接字符串和自己用文件 IO 写入,但是用javapoet要更方便得多。


private MethodSpec createConstructor(List<AnnotatedRandomElement> randomElements) {AnnotatedRandomElement firstElement = randomElements.get(0);MethodSpec.Builder builder = MethodSpec.constructorBuilder().addModifiers(Modifier.PUBLIC).addParameter(TypeName.get(firstElement.getElement().getEnclosingElement().asType()), "target");for (int i = 0; i < randomElements.size(); i++) {addStatement(builder, randomElements.get(i));}return builder.build();}


private void addStatement(MethodSpec.Builder builder, AnnotatedRandomElement randomElement) {builder.addStatement(String.format("target.%1s",randomElement.getElementName().toString(),randomElement.getRandomValue()));}


private TypeSpec createClass(String className, MethodSpec constructor) {return TypeSpec.classBuilder(className + "_Random").addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(constructor).build();}


private String getPackage(String qualifier) {return qualifier.substring(0, qualifier.lastIndexOf("."));}


private String getClassName(String qualifier) {return qualifier.substring(qualifier.lastIndexOf(".") + 1);}


通过以上几行代码,创建了类文件。在类的构造函数中添加参数(target), 并为每一个被注解元素添加语句"target.%1$s = %2$s",最后通过javaFile.writeTo(filer)完成文件写入。

3. 调用生成类的方法

在 lib_api 中新建一个类:RandomUtil.java,添加注入方法:


public static void inject(Object object) {Class bindingClass = Class.forName(object.getClass().getCanonicalName() + "_Random");Constructor constructor = bindingClass.getConstructor(object.getClass());constructor.newInstance(object);}


这里利用反射找到了以“Object 类名_Random”命名的生成类,并调用它


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


的构造方法。而在我们之前的注解处理器中,我们已在生成类的构造方法中实现了属性的赋值操作。

4. 使用生成类

在 app module 中依赖刚才创建的库:


implementation project(':lib_annotations')implementation project(':lib_api')annotationProcessor project(':lib_compiler')


在 Activity 中的使用


public class MainActivity extends AppCompatActivity {@RandomInt(minValue = 10, maxValue = 1000)int mRandomInt;


@RandomStringString mRandomString;


@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);


RandomUtil.inject(this);


Log.i("RandomInt ==> ", mRandomInt + "");Log.i("RandomString ==> ", mRandomString);}}


编译,运行,查看结果:


RandomInt ==>: 700RandomString ==>: HhRayFyTtt


被注解的元素成功被自动赋值,说明注入成功。注解处理的使用可参考完整的demo地址

调试

注解处理器的 debug 跟普通的代码 debug 有点不同:


在当前工程路径下输入命令


gradlew --no-daemon -Dorg.gradle.debug=true :app:clean :app:compileDebugJavaWithJavac


并在Edit Configurations中新添加一个远程配置(remote),名字随意,端口为 5005。 然后点击 debug 按钮,就可以连接上远程调试器进行 Annotation 的调试了。


最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如 Handler 机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。


最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司 19 年的面试题,把技术点整理成了视频和 PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。


还有 高级架构技术进阶脑图、Android 开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。


Android学习PDF+架构视频+面试文档+源码笔记


【Android 核心高级技术 PDF 文档,BAT 大厂面试真题解析】



【算法合集】



【延伸 Android 必备知识点】



【Android 部分高级架构视频学习资源】


**Android 精讲视频领取学习后更加是如虎添翼!**进军 BATJ 大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务 Curd 而已!现如今市场上初级程序员泛滥,这套教程针对 Android 开发工程师 1-6 年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶 Android 中高级、架构师对你更是如鱼得水,赶快领取吧!


【Android 进阶学习视频】、【全套 Android 面试秘籍】关注我【主页简介】或者【简信我】查看免费领取方式!

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
字节三面:ButterKnife为什么执行效率为什么比其他注入框架高?它的原理是什么