2019-11-23 23:09:29
该系列介绍自定义注解,完成如下功能。
- @BindView 代替 findViewById
- @ClickResponder 代替 setOnClickListener
- @LongClickResponder 代替 setOnLongClickListener
- @IntentValue 代替 getIntent().getXXX
- @UriValue 代替 getQueryParameter
- @BroadcastResponder 代替 registerReceiver
- @RouterModule、@RouterPath 来进行反依赖传递调用
使用编译时注解,生成辅助类来完成这些操作,尽量少的使用的反射功能。
该系列源码在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 编写提供给用户调用的方法
注解的声明
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) //该注解可以在运行时通过反射拿到
/**
* 用来代替findViewById
*/
public @interface BindView {
int value() ; //这里可以声明默认值 int value() default -1;
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
/**
* 用来代替setOnClickListener
*/
public @interface OnClick {
int id();
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
/**
* 用来代替ssetOnLongClickListener
*/
public @interface OnLongClick {
int id();
boolean result() default true;
}
反射
java中的反射真是个毁誉参半的东西,至于怎么用,自己搜一下吧。使用反射调用非静态方法时,第一个参数是方法所在类的实例;调用静态方法时,第一个参数传null即可。
实现
新建Activity,随便写点布局、控件什么的。
使用方式和Butterknife差不多。
@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中对注解进行操作,
private void initAnnotation() {
//反射获取所有声明的字段
Field fields[] = this.getClass().getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
//判断字段是否有BindView注解
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 onLongClick = method.getAnnotation(OnLongClick.class);
if (onLongClick != null) {
//对该方法的注解值对应的控件 设置OnLongClickListener
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 onClick = method.getAnnotation(OnClick.class);
if (onClick != null) {
//对该方法的注解值对应的控件 设置OnClickListener
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 这么个东西
以上