该系列介绍自定义注解,完成如下功能。
使用编译时注解,生成辅助类来完成这些操作,尽量少的使用的反射功能。
该系列源码在https://github.com/huangyuanlove/AndroidAnnotation
本章先介绍一丢丢注解相关的东西,并且实现运行时注解
常见的注解:
@Override:被标记的方法一定是重写的父类方法,反之不一定;
@Deprecated:被标记的方法为过时方法,调用该方法时编辑器会有警告
@SuppressWarnings 指示编译器去忽略注解中声明的警告。
元注解 :作用在其他注解的注解
@Retention - 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。值有三种类型(RetentionPolicy.SOURCE;RetentionPolicy.CLASS;RetentionPolicy.RUNTIME)
@Target - 标记这个注解应该是哪种 Java 成员。常见的值ElementType.TYPE(作用于类)、ElementType.FIELD(作用于字段)、ElementType.METHOD(作用于方法)等
更多关于注解的东西自己搜一下,搜不到的话不要往下看了
太长不看系列总结
- 声明注解
- 解析运行时注解
2.1 获取类的属性和方法
2.2 找到添加注解的属性或者方法
2.3 做自定义注解需要做的事情
- 解析编译时注解(需要编写注解处理器)
3.1 注册注解处理器(@AutoService)
3.2 拿到注解的属性和方法
3.3 生成辅助文件的内容(通常会使用javapoet)
3.4 写入文件
3.5 编写提供给用户调用的方法
注解的声明
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME)
public @interface BindView { int value() ; }
|
1 2 3 4 5 6 7 8 9
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME)
public @interface OnClick { int id(); }
|
1 2 3 4 5 6 7 8 9
| @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME)
public @interface OnLongClick { int id(); boolean result() default true; }
|
反射
java中的反射真是个毁誉参半的东西,至于怎么用,自己搜一下吧。使用反射调用非静态方法时,第一个参数是方法所在类的实例;调用静态方法时,第一个参数传null即可。
实现
新建Activity,随便写点布局、控件什么的。
使用方式和Butterknife差不多。
1 2 3 4 5 6 7 8 9 10 11 12
| @BindView(value = R.id.test_runtime_annotation) Button testRuntimeAnnotation;
@OnLongClick(id = R.id.test_runtime_annotation) void testRuntimeAnnotationOnLongClick(View v) { Toast.makeText(this, "测试运行时注解:onLongClick", Toast.LENGTH_LONG).show(); }
@OnClick(id = R.id.test_runtime_annotation) void setTestRuntimeAnnotationOnClick(View v) { Toast.makeText(this, "测试运行时注解:onClick", Toast.LENGTH_LONG).show(); }
|
我们需要在onCreate中对注解进行操作,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
| private void initAnnotation() { Field fields[] = this.getClass().getDeclaredFields();
for (Field field : fields) { field.setAccessible(true); BindView bindView = field.getAnnotation(BindView.class); if (bindView != null) { try { field.set(this, findViewById(bindView.value())); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
Method methods[] = this.getClass().getDeclaredMethods(); for (Method method : methods) { method.setAccessible(true); OnLongClick onLongClick = method.getAnnotation(OnLongClick.class); if (onLongClick != null) { findViewById(onLongClick.id()).setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { try { method.invoke(v.getContext(), v);
} catch (Exception e) { e.printStackTrace(); } return onLongClick.result();
} }); } OnClick onClick = method.getAnnotation(OnClick.class); if (onClick != null) { findViewById(onClick.id()).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { method.invoke(v.getContext(), v); } catch (Exception e) { e.printStackTrace(); } } }); } } }
|
上面就简单的实现了o’nClick,onLongClick,BindView注解,只不过使用的运行时注解,通过反射来对字段和注解进行操作。如果量大的话,反射会消耗性能,我们可以通过注解在编译期间生成辅助类来进行操作,比如 PermissionsDispatcher
下一篇介绍一下这种方式,需要用到 javapoet 这么个东西
以上