当前最火热的各种安卓框架:ButterKnife、Retrofit、EventBus、GreenDao等全部是使用了安卓注解Annotation技术,可见注解确实是有它独特的优势,知其然而知其所以然,才能在开发中更得心应手!平时总有人会说这么多的框架只要会用就足够了没有必要浪费时间看源码是如何实现的,我想说:请不要为自己的懒惰找借口!!我自己的体会:在看了很多框架源码后,有一种茅塞顿开的感觉,对自己的技术提高也非常快!
一、基础讲解
首先先来看一个例子,有一个大概的了解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
我对注解做了一个概括,首先注解分为两种:元注解和自定义注解
元注解:java提供的注解,可以供我们使用(其实就是为了我们自定义注解使用),有四个如下:
1、@Retention:声明注解的生效范围,有三个值:SOURCE(源码生效)、CLASS(编译时生效,ButterKnife就是此值)、RUNTIME(运行时生效,EventBus3.0为此值)
2、@Target:声明一个类中,注解所要标注的对象,包括变量(Field)、方法(Method)、类(Class)
3、@Inherited:标明所修饰的注解,在所作用的类上,是否可以被继承。
4、@Documented:如其名,javadoc的工具文档化,一般不关心。
自定义注解:通过元注解的限制,实现自己想要的注解,在项目中自定义注解又可分为两类:
1、编译时注解:通过@Retention(RetentionPolicy.CLASS)修饰,ButterKnife为编译时注解,编译时注解需要有相应的注解解析器Process,用于在编译时操作注解
2、运行时注解:通过@Retention(RetentionPolicy.RUNTIME)修饰,EventBus3.0为运行时注解,运行时注解需要通过反射,在代码中操作注解
二、自定义运行时注解模仿ButterKnife
1、首先看下我的项目结构:
BindView:用于取代findViewById()
MethodInfo:给方法添加注解
2、BindView注解源码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
3、MethodInfo注解源码:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodInfo {
String author() default "haoxinlei";
String date();
int version() default 1;
}
4、MainActivity:注释已经给的很清楚,不在赘述,运行时注解用到了java反射的技术,不熟悉反射的小伙伴可以先去啃一下反射机制,ps:反射真的很重要,在如今的各种框架中,几乎都用到了反射!
public class MainActivity extends AppCompatActivity {
@BindView(R.id.text)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iocView(); //通过反射,给控件设置id
textView.setText("nihao"); //检测给控件设置id是否成功
test(); //通过反射,给方法加注释
}
private void iocView() {
try {
//反射拿到当前MainActivity的Class对象
Class cls = Class.forName("com.test.annotation.MainActivity");
//获取MainActivity的所有的变量
Field[] fields = cls.getFields();
for (Field field:fields){
//遍历MainActivity的所有的变量,获取每一个变量的注释
BindView bindView = field.getAnnotation(BindView.class);
if (bindView!=null){
//通过注释拿到id,bindView.value()就是上边的R.id.text
textView = (TextView) findViewById(bindView.value());
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
@MethodInfo(author = "haha", date = "2017.9.19", version = 2)
public void test() {
try {
Class cls = Class.forName("com.test.annotation.MainActivity");
//运行时注解,反射获取当前MainActivity的所有的method
Method[] methods = cls.getMethods();
for (Method method : methods) {
//获取每个方法的注解
MethodInfo methodInfo = method.getAnnotation(MethodInfo.class);
if (methodInfo != null) {
//最终打印出:haha2017.9.192,与test方法的注解相对应
Toast.makeText(this, methodInfo.author() + methodInfo.date() + methodInfo.version(), Toast.LENGTH_SHORT).show();
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
今天只总结了一下运行时注解的例子,大家也看到了,运行时注解需要依靠反射技术,那么在ButterKnife中如果全部使用反射那肯定耗费巨大的性能,所以ButterKnife使用编译时注解,在编译阶段就完成了所有的工作,后续我会继续总结ButterKnife的原理,以及真正模仿ButterKnife使用编译时注解。