本篇博文针对具备注解基础的读者,主要讲解如何进行自定义注解。关于注解的具体基础知识点,网上这方面的学习资料非常多,可自行学习。
注解目前在主流的框架,比如Android中的Glide、Retrofit;Java Web方向的Spring等都有大量的使用。在给开发者带来巨大方便的同时,作为开发者有必要了解学习注解及其自定义,甚至可以自定义自己的注解库呦。
以下分析均在Android Studio中进行;
1、基础
上图来源于网络。感谢。上图是对Java注解知识点的总结,已经十分的全面了。如果你只是想自定义一个运行时的注解及处理器,上述知识点已经足够。但是如果你想要自定义一个编译型注解,那你还需要学习更多东西,比如AbstractProcessor、APT、JavaPoet等等,稍后会介绍。
2、区别
运行时注解与编译时注解的区别是什么呢?
a)保留阶段不同。运行时注解保留到运行时,可在运行时访问。而编译时注解保留到编译时,运行时无法访问。
b)原理不同。运行时注解是Java反射机制,而编译时注解通过APT、AbstractProcessor。
c)性能不同。运行时注解由于使用Java反射,因此对性能上有影响。编译时注解对性能没影响。这也是为什么ButterKnife从运行时切换到了编译时的原因。
d)产物不同。运行时注解只需自定义注解处理器即可,不会产生其他文件。而编译时注解通常会产生新的Java源文件。
3、运行时注解
相对于编译时注解,运行时注解要简单的多。运行时注解的自定义只有两个步骤:
自定义注解 + 注解处理器(会用到java反射机制);
3.1、自定义运行时注解
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RuntimeBind {
int value();
}
RetentionPolicy.RUNTIME:表明这是一个运行时的注解,如果是编译时注解,则应该是RetentionPolicy.CLASS。
ElementType.FIELD:表明该注解是用于域的。
3.2、自定义注解处理器
public class RuntimeAnnotationProcessor {
public static void bind2(Activity activity){
if (activity==null) return;
Field[] fields = activity.getClass().getDeclaredFields();
if (fields == null || fields.length == 0) return;
for(int i=0;i<fields.length;i++){
if (fields[i].isAnnotationPresent(RuntimeBind.class)){
int resId = fields[i].getAnnotation(RuntimeBind.class).value();
fields[i].setAccessible(true);
try {
fields[i].set(activity,activity.findViewById(resId));
fields[i].setAccessible(false);
} catch (IllegalAccessException e) {
e.printStackTrace();
fields[i].setAccessible(false);
}
}
}
}
}
可以看到,代码量还是很小的。首先,我们通过反射获取到当前Activity都有哪些字段,然后再判断每个字段是不是在使用RuntimeBind注解,如果使用了,则获取该注解的值,同时将该注解的值赋值给对应的字段就可以了。
3.3、使用运行时注解处理器
public class MainActivity extends AppCompatActivity {
@RuntimeBind(R.id.tv_runtime)
TextView tv_runtime;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RuntimeAnnotationProcessor.bind2(this);
tv_runtime.setText("运行时注解成功");
}
}
在使用的时候,只需要调用RuntimeAnnotationProcessor.bind2(..)即可完成繁琐的findViewById工作了。是不是很方便。但是,如果存在大量的通过运行时注解,则在一定程度上会影响程序的性能的。因此,编译时注解的优势就发挥出来了。
4、编译时注解
编译时注解总体结构:编译时注解 + 注解处理器(基于AbstractProcessor) + APT + JavaPoet(自定义Java源文件会用到) + auto-service(处理器注册);
4.1、基础准备
a)什么是APT?
APT(Annotation Processing Tool)是javac内置的工具,用于在编译时期扫描和处理注解信息。它对源代码文件进行检测找出其中的Annotation,根据注解自动生成代码(调用注解处理器的Process方法生成源文件)。 Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
b)APT插件(工具)
android-apt:由一位开发者自己开发的apt框架,源代码托管在这里,随着Android Gradle 插件 2.2 版