注解是什么?
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释;
元注解一共有四种:
@Retention,@Target,@Inherited,@Documented
个人理解元注解就是注解之上还有注解
为什么要使用注解?
每当你创建描述符性质的类和接口的时,一旦其中包含重复性工作,就可以考虑简化与自动化该过程。
如果你想把某个方法声明为服务,那么使用Annotation会更好一些,因为这种情况下需要注解和方法紧密耦合起来。
注解的实现
最早使用ButterKnife(俗称剃须刀),最喜欢的就是view的绑定写法,简单明了如下
@BindView(R.id.test_btn)
Button btn ;
@BindView(R.id.test_txt)
TextView mTextView;
@Retention
这个枚举决定了Retention注解应该如何去保持,也可理解为Rentention 搭配 RententionPolicy使用。RetentionPolicy有3个值:CLASS RUNTIME SOURCE
按生命周期来划分可分为3类:
1、RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
2、RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
3、RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
@Target 提供不同的类型设置不同的属性
ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
ElementType.CONSTRUCTOR 可以给构造方法进行注解
ElementType.FIELD 可以给属性进行注解
ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
ElementType.METHOD 可以给方法进行注解
ElementType.PACKAGE 可以给一个包进行注解
ElementType.PARAMETER 可以给一个方法内的参数进行注解
ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
现在咋们本地自定义一个类似@BindView的
注解
@Retention(RetentionPolicy.RUNTIME) //咋们需要使用并生成class文件的 @Target(ElementType.FIELD) //表示当前的注解是给属性来使用 public @interface BindView { int value(); //int 的属性值 }
添加一个点击事件的注解
@Retention(RetentionPolicy.RUNTIME) //咋们需要使用并生成class文件的 @Target(ElementType.METHOD) //说明当前的注解是提供给方法使用的 public @interface OnClick { int[] value(); //数组 }
生成完注解,看看注解该如何使用
public class Main extends Activity { public static final String cTAG = "MainTest"; Handler mHandler; int count = 5; @BindView(R.id.test_btn) Button btn ; @BindView(R.id.test_txt) TextView mTextView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); ButterKnifeProcess.bind(this); //注解需要初始化才能使用,就像ButterKnife.bind(this)一样 } /** * 绑定点击事件 */ @OnClick({R.id.test_btn}) public void aaa(View view) { mTextView.setText("註解"); } }
XML
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <TextView android:id="@+id/test_txt" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="hello" /> <Button android:id="@+id/test_btn" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
注解的具体实现
public class ButterKnifeProcess {
/** * 绑定Activity */ public static void bind(final Activity activity) {
Class annotationParent = activity.getClass(); //获取当前的class Field[] fields = annotationParent.getDeclaredFields(); //返回一个数组,其中包含所有声明字段的对象
Method[] methods = annotationParent.getDeclaredMethods();//返回一个数组,其中包含所有声明的方法
for (final Method method : methods) { //遍历方法 OnClick clickMethod = method.getAnnotation(OnClick.class); //通过注解获取当前点击的方法 if (clickMethod != null && clickMethod.value().length != 0) { for (int id : clickMethod.value()) { final View view = activity.findViewById(id); //获取当前的VIew view.setOnClickListener(new View.OnClickListener() { //设置点击事件 @Override public void onClick(View v) { try { method.invoke(activity, view); //反射该方法 } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } } }); } } }
try { for (final Field field : fields) { //遍历当前类所有的对象 BindView bindView = field.getAnnotation(BindView.class); //获取bindview对象 if (bindView != null) { View view = activity.findViewById(bindView.value()); //在这里设置activity.findViewById field.setAccessible(true); //启用访问安全检查的开关 field.set(activity, view); } } } catch (IllegalAccessException e) { e.printStackTrace(); }
}
}
总结
写法简单明了,方便使用,但是一直遍历反射,比较耗性能。而ButterKnife 整个过程是在项目编译阶段完成,相比之下这种在运行时通过反射的方式就不建议使用了,但是可以理解注解的使用方式,方便以后对应第三方插件注解的理解