内容简介:本文主要是对编译时注解展开讨论。Android端对自定义注解的应用程度远没有达到Java Web端的那种程度,而且市面上也没有一个专注于抽象编译时注解底层技术的库。在真实开发场景中,如果想要实现一个利用编译时注解的功能,往往需要花大量精力来处理APT这块的繁琐逻辑。例如,遍历所有注解相关类然后生成一定规律的代码。这些繁琐操作阻碍了大家使用注解的热情。设想一下,如果使用编译时注解只需要增加一两行代码,那你会不会更愿意使用注解呢?所以,我们在想是否能够对编译时注解做进一步的抽象,帮助开发者将重点放在具体的功能
前言
本文主要是对编译时注解展开讨论。Android端对自定义注解的应用程度远没有达到Java Web端的那种程度,而且市面上也没有一个专注于抽象编译时注解底层技术的库。
在真实开发场景中,如果想要实现一个利用编译时注解的功能,往往需要花大量精力来处理APT这块的繁琐逻辑。例如,遍历所有注解相关类然后生成一定规律的代码。这些繁琐操作阻碍了大家使用注解的热情。设想一下,如果使用编译时注解只需要增加一两行代码,那你会不会更愿意使用注解呢?
所以,我们在想是否能够对编译时注解做进一步的抽象,帮助开发者将重点放在具体的功能实现上,而不是处理编译时注解的繁琐操作上?
Nara的诞生
在动手之前,需要先弄清楚大家利用注解都做了哪些事情。清楚注解的使用场景,能帮助我们抽象出一个更好用的注解框架。于是,我们调研了Android端上常用的注解框架,看看他们利用注解都做了什么事情?
-
ButterKnife:例如@BindViews,ButterKnife在编译期,利用@BindView将控件ID和类属性建立对应关系。然后在页面启动时,根据控件ID通过findViewById方法将控件赋值到类属性上。
-
EventBus:通过@Subscribe对普通方法进行标记。然后,EventBus在编译期通过@Subscribe将这些方法收集起来,作为EventBus的订阅方法。
-
Retrofit:通过反射方式处理注解,不再本文范畴。
编译时注解的主要用途在于,收集注解并通过注解信息生成一些有规律的代码。
而这些有规律的代码,一般都是可以通过其他方式来间接实现的。比如说,ButterKnife的属性赋值可以通过反射来实现,EventBus的方法收集可以直接换个写法。所以,如果要对编译时注解做进一步抽象,我们认为可以从注解收集方面入手。由此,Nara注解收集器诞生了。
API的设计
对于底层库而言,设计一套好用的API是很重要的。从库的使用者角度看,希望注解收集器能够帮助我们实现哪些功能呢?
- 注解的作用范围:通过分析市面上编译时注解的使用场景,发现对注解的应用主要在于类,方法和属性上。
- 自定义注解:作为注解抽象库,如果无法支持自定义注解,那对可扩展性简直就是致命的打击。所以需要提供自定义注解功能。
- 注解收集:注解收集作为核心功能,必须要提供一套好用的收集操作。我们考虑用链式Builder来完成注解收集功能。
最终设计的API如下:
/** * 自定义注解API **/ @Retention(RetentionPolicy.SOURCE) @Target(ElementType.TYPE, ElementType.METHOD, ElementType.FIELD) @ShadowBinding public @interface CustomAnnotation { String value() default ""; } /** * 注解的使用API **/ @CustomAnnotation // 类 public class AnnotationClass extends Activity{} @CustomAnnotation // 函数 public <T> T AnnotationMethod(int p1, String p2, AnnotationClass p3, T p4){ Log.e("souche", "test2(" + "p1(" + p1 + "),p2(" + p2 + "),p3(" + p3 + "),p4(" + p4 + "));"); return p4; } @CustomAnnotation // 成员属性 public List<AnnotationClass> annotationField; /** * 收集注解的使用API,以Class的收集举例 **/ List<ClassDesc> listClass = Nara .findClass(CustomAnnotation.class) // 查找类,参数为其所在注解 .withExtends(Activity.class) // 筛选条件:查找的类,需要继承Activity类 .withAnnotations(Collect.class) // 筛选条件:查找的类,需要被@Collect注解 .filter(new AnnotationFilter<ClassDesc>() { // 自定义筛选条件:return true 表示过滤掉当前类 @Override public boolean doFilter(ClassDesc obj) { return false; } }) .list(); // 返回符合条件的类集合
实现的痛点
一个优秀的底层库,应该将复杂实现隐藏起来,并且做到实现对于上层使用者透明。下面简单分享下实现Nara时,遇到的几个技术痛点。
对泛型的支持
在收集注解信息时,会遇到方法参数或者属性中带有泛型信息的情况。为了收集这些泛型信息,我们利用了Gson实现的TypeToken泛型Type转化工具。将TypeToken的相关代码拷贝到了Nara内部。Gson获取Type的方法如下:
Type type = new TypeToken<clazz>() {}.getType();
被注解对象的包级作用域问题
Nara注解收集是支持包级作用域的信息收集的。这里就存在一个问题,包作用域的类,方法和属性是不允许跨包访问的。那么,如何在不同包下获取包级作用域下的信息呢?我们采取的方式是利用编译时注解生成被注解类的同包名下的类,然后将包级作用域的信息用public的形式暴露出来。如下所示:
// 例如包级作用域的属性 package com.example.souche.annotation; public class People { @CustomAnnotation String name; } // 为了将People.name的get和set方法的作用域暴露出来 // 我们在编译时,生成了public作用域的get和set方法 package com.example.souche.annotation; /** This class is generated by SouChe Annotation Collection, do not edit. */ public class CompilerCollection$AdaptableClass1540292058755_29 extends com.souche.android.annotation.core.FieldDesc.BeanMethod { @Override public void set(Object... params){ if (params.length != 1) throw new IllegalArgumentException("set argument format error!"); com.example.souche.annotation.People.name = (java.lang.String)params[0]; }; @Override public Object get(Object... params){ if (params.length != 0) throw new IllegalArgumentException("get argument format error!"); return com.example.souche.annotation.People.name; }; }
组件化下的编译时注解问题
我们团队是采用的是模块化的开发形式,模块将代码将aar包上传到maven仓库,主工程再通过gradle来依赖模块aar包。这样就会存在一个问题,由于编译时注解的作用范围是源码级别的,所以无法对aar包中的注解进行收集。
我们的解决思路是,如果模块需要使用Nara注解收集器,在模块打包时,就将Nara相关代码打进aar包,然后在主工程打包时,通过gradle插件扫描整个app的字节码,找到Nara相关代码,将这些代码整合起来,生成一个Nara初始化主类。总体打包流程如下:
框架愿景
Nara是对编译时注解的上层抽象,我们希望通过Nara来降低编译时注解的开发成本。让使用者把精力放在逻辑实现上,而不是在注解信息收集上。
以上所述就是小编给大家介绍的《Nara: 大搜车的注解收集器》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
New Dark Age
James Bridle / Verso Books / 2018-7-17 / GBP 16.99
As the world around us increases in technological complexity, our understanding of it diminishes. Underlying this trend is a single idea: the belief that our existence is understandable through comput......一起来看看 《New Dark Age》 这本书的介绍吧!