调用其他app 的lib_ButterKnife执行效率为什么比其他注入框架高?它的原理是什么...

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

心理分析: ButterKnife框架一直都是使用,很少又开发者对butterknife深入研究的,既然你是面试Android高级岗位,自然需要有相应被问到原理的准备,面试官想问你对注解处理器了解多少,Android编译流程有多少认识

**求职者:**应该从 注解处理器原理 与优势说起,肯定注解处理器对解放生产力的作用。然后可以引申常见的 Butterknife,Dagger2,DBFlow。这才是加分项

优势

  1. 我们平常在使用Java进行开发Android时,经常会需要写很多重复冗余的样板代码,开发中最常见的一种,就是findViewById了,如果一个界面有很多View,写起来那叫一个要死要死。于是我们注解处理器可以帮助解决冗余的代码的,
  2. 由于是在编译器进行生成的代码,并不是通过反射实现,所以性能优势是非常高的
  3. 加快开发速度,由于减少了写繁琐的代码,会对项目进度起有利的作用

接下来我们一起来看注解处理的原理

在android开发中,比较常用到的第三方库中,有不少用到了 注解处理器(Annotation Processor)。 比较常见的就有 Butterknife,Dagger2,DBFlow 等。

注解

Java中存在不少关于注解的Api, 比如@Override用于覆盖父类方法,@Deprecated表示已舍弃的类或方法属性等,android中又多了一些注解的扩展,如@NonNull, @StringRes, @IntRes等。

代码自动生成

使用代码自动生成,一是为了提高编码的效率,二是避免在运行期大量使用反射,通过在编译期利用反射生成辅助类和方法以供运行时使用。

注解处理器的处理步骤主要有以下:

  1. 在java编译器中构建
  2. 编译器开始执行未执行过的注解处理器
  3. 循环处理注解元素(Element),找到被该注解所修饰的类,方法,或者属性
  4. 生成对应的类,并写入文件
  5. 判断是否所有的注解处理器都已执行完毕,如果没有,继续下一个注解处理器的执行(回到步骤1)
8d6969ce4d2765338cbd2dbd004dd672.png

Butterknife注解处理器的例子

Butterknife的注解处理器的工作方式如下:

  1. 定义一个非私有的属性变量
  2. 添加该属性变量的注解和传入id
  3. 调用Butterknife.bind(..)方法。

当你点击Android Studio的Build按钮时,Butterknife先是按照上述步骤生成了对应的辅助类和方法。在代码执行到bind(..)方法时,Butterknife就去调用之前生成的辅助类方法,完成对被注解元素的赋值操作。

自定义注解处理器

了解了基本的知识点后,我们应该尝试去使用这些技巧。 接下来是实践时间,我们来开发一个简单的例子,利用注解处理器来自动产生随机数字和随机字符串。

  1. 首先创建一个project。
  2. 创建lib_annotations, 这是一个纯java的module,不包含任何android代码,只用于存放注解。
  3. 创建lib_compiler, 这同样是一个纯java的module。该module依赖于步骤2创建的module_annotation,处理注解的代码都在这里,该moduule最终不会被打包进apk,所以你可以在这里导入任何你想要的任意大小依赖库。
  4. 创建lib_api, 对该module不做要求,可以是android library或者java library或者其他的。该module用于调用步骤3生成的辅助类方法。
86092d24d8b792cebbff05c86cfbdfbc.png
7063d5d7762323979a5e44cad7b87dec.png

1. 添加注解

在lib_annotations中添加两个注解:RandomString, RandomInt,分别用于生成随机数字和随机字符串:

@Retention(CLASS)@Target(value = FIELD)public @interface RandomString {}复制代码@Retention(CLASS)@Target(value = FIELD)public @interface RandomInt { int minValue() default 0; int maxValue() default 65535;}复制代码
  • @interface 自定义注解,使用 @interface 作为类名修饰符
  • @Target 该注解所能修饰的元素类型,可选类型如下:
public enum ElementType { TYPE, //类 FIELD, //属性 METHOD, //方法 PARAMETER, //参数 CONSTRUCTOR, //构造函数 LOCAL_VARIABLE,  ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE; private ElementType() { }}复制代码
  • @Retention 该注解的保留策略,有三种选项:
public enum RetentionPolicy { SOURCE, //被编译器所忽略 CLASS, //被编译器保留至类文件,但不会保留至运行时 RUNTIME //保留至类文件,且保留至运行时,能在运行时反射该注解修饰的对象}复制代码

2. 注解处理器

真正处理注解并生成代码的操作都在这里。 在写代码之前我们需要先导入两个重要的库,以及我们的注解模块:

compile 'com.google.auto.service:auto-service:1.0-rc4'compile 'com.squareup:javapoet:1.9.0'implementation project(':lib_annotations')复制代码

新建类RandomProcessor.java:

@AutoService(Processor.class)public class RandomProcessor extends AbstractProcessor{ @Override public synchronized void init(ProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); } @Override public SourceVersion getSupportedSourceVersion() { return super.getSupportedSourceVersion(); } @Override public Set getSupportedAnnotationTypes() { return super.getSupportedAnnotationTypes(); } @Override public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) { return false; }}复制代码
  • @AutoService @AutoService(Processor.class)会告诉编译器该注解处理器的存在,并在编译时自动在META-INF/services下生成javax.annotation.processing.Processor文件,文件的内容为
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> 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 getSupportedAnnotationTypes() { Set 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()); } annotatedElementMap.get(qualifier).add(randomElement);}复制代码

生成类文件

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

for (Map.Entry 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 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.%1$s = %2$s

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/311629.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法---会议最大安排问题

算法—会议最大合理安排问题 参考&#xff1a;趣学算法 代码&#xff1a; #include <stdio.h> #include <stdlib.h> typedef struct meet {int beg;//开始int end;//结束int num;//会议编号 }meet; int cmp44(meet m1,meet m2) {//越早结束的越优先&#xff0c;…

小cookie,大智慧

Cookie是什么&#xff1f;cookies是你访问网站时创建的数据片段文件&#xff0c;通过保存浏览信息&#xff0c;它们使你的在线体验更加轻松。使用cookies&#xff0c;可以使你保持在线登录状态&#xff0c;记录你的站点偏好&#xff0c;并为你提供本地化支持。First-party cook…

java 最少使用(lru)置换算法_LRU算法详解及最简单的Java实现

更多内容&#xff0c;欢迎关注微信公众号&#xff1a;全菜工程师小辉~LRU(Least recently used&#xff0c;最近最少使用)算法根据数据的历史访问记录来进行淘汰数据&#xff0c;其核心思想是“如果数据最近被访问过&#xff0c;那么将来被访问的几率也更高”。LRU算法的表现新…

word List 48

word List 48 如果存在什么问题&#xff0c;欢迎批评指正!谢谢&#xff01;

一文读懂常用开源许可证

社区时常为流行产品中有争议的开源许可证而感到震惊&#xff0c;这引起各方关注&#xff0c;纷纷争论何为真正的开源许可证。去年&#xff0c;Apache 基金会&#xff08;Apache Foundation&#xff09;禁止使用 Facebook React 那些具有争议的专利组件&#xff0c;这引发了轩然…

[蓝桥杯2015初赛]手链样式-思维+next_permutation枚举(好题)

题目描述 小明有3颗红珊瑚&#xff0c;4颗白珊瑚&#xff0c;5颗黄玛瑙。 他想用它们串成一圈作为手链&#xff0c;送给女朋友。 现在小明想知道&#xff1a;如果考虑手链可以随意转动或翻转&#xff0c;一共有多少不同的组合样式&#xff1f; 输出 请你输出该整数。不要输出任…

word List 49

word List 49 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢!

(四)开源C# WPF控件库《AduSkin – UI》

微信公众号&#xff1a;【Dotnet9的博客】&#xff0c;网站&#xff1a;【Dotnet9】&#xff0c;问题或建议&#xff1a;【请网站留言】&#xff0c; 如果对您有所帮助&#xff1a;【欢迎赞赏】。https://dotnet9.com追求极致&#xff0c;永臻完美A Beautiful WPF Control UI一…

python输入数据爬取_python根据用户需求输入想爬取的内容及页数爬取图片方法详解...

本次小编向大家介绍的是根据用户的需求输入想爬取的内容及页数。 主要步骤&#xff1a; 1.提示用户输入爬取的内容及页码。 2.根据用户输入&#xff0c;获取网址列表。 3.模拟浏览器向服务器发送请求&#xff0c;获取响应。 4.利用xpath方法找到图片的标签。 5.保存数据。 代码…

word List 50

word List 50 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

单点突破,击穿阈值,DevOps转型你需要这样做

在上篇文章里&#xff0c;我提到了如何通过对价值流进行分析、拆解关键要素指标&#xff0c;并通过缩减处理时间PT、降低前置时间LT、提高完成&准确的百分比&#xff08;C&A%&#xff09;&#xff0c;实现企业研发效能10倍速提升。大家点击回看这篇文章《以埃隆马斯克“…

双向dcdc变换器simulink仿真_二极管箝位五电平变换器Simulin仿真

● 本期为二极管箝位五电平变换器的基本Simulink仿真&#xff0c;只包含其SPWM调制。感谢公众号一位好友“一叶知秋”提供Simulink模型并分享。01二极管箝位五电平变换器调制方法传统的变换器存在高的电压变化率和共模电压&#xff0c;且波形谐波含量较大&#xff0c;使得输出滤…

在.NET Core中使用MachineKey

在.NET Core中使用MachineKey在上篇文章中&#xff0c;我介绍了 Cookie是基于 MachineKey生成的&#xff0c; MachineKey决定了 Cookie生成的算法和密钥&#xff0c;并如果使用多台服务器做负载均衡时&#xff0c;必须指定一致的 MachineKey。但在 .NETCore中&#xff0c;官方似…

cg word List 1

cg word List 1 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢!

[蓝桥杯2016初赛]剪邮票-dfs+next_permutation(好题)

题目描述 如下图, 有12张连在一起的12生肖的邮票。现在你要从中剪下5张来&#xff0c;要求必须是连着的。&#xff08;仅仅连接一个角不算相连&#xff09; 比如&#xff0c;下面两张图中&#xff0c;粉红色所示部分就是合格的剪取。 请你计算&#xff0c;一共有多少种不同…

cg word List2

cg word List 2 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

如何扩展分布式日志组件(Exceptionless)的日志通知?

作者&#xff1a;justmine头条号&#xff1a;大数据与云原生微信公众号&#xff1a;大数据与云原生创作不易&#xff0c;在满足创作共用版权协议的基础上可以转载&#xff0c;但请以超链接形式注明出处。为了方便阅读&#xff0c;微信公众号已按分类排版&#xff0c;后续的文章…

hadoop hive集群_基于伪分布式Hadoop搭建Hive平台详细教程

一、搭建环境的前提条件环境&#xff1a;Linux系统Hadoop-2.6.0MySQL 5.6apache-hive-2.3.7这里的环境不一定需要和我一样&#xff0c;基本版本差不多都ok的&#xff0c;所需安装包和压缩包自行下载即可。但是注意hive和hadoop都是2.x系列版本的。这里提供一个我下载的hive版本…

cg word List 3

cg word List 3 如果存在什么问题&#xff0c;欢迎批评指正&#xff01;谢谢&#xff01;

.NET Core开发实战(第7课:用Autofac增强容器能力)--学习笔记(上)

07 | 用Autofac增强容器能力&#xff1a;引入面向切面编程&#xff08;AOP&#xff09;的能力这一节讲解使用第三方框架来扩展依赖注入容器什么情况下需要我们引入第三方容器组件&#xff1f;大部分情况下&#xff0c;默认的容器组件足够使用当需要一些非常特殊的场景如下&…