Java--Annotation之旅 ( Runtime反射篇
2020-11-11 本文已影响0人
初夏的雪
相信经过前面两篇文章的学习,我们对Java Annotation 有了一定的掌握了。仔细的同学注意到了,我们自定义的注解的保留级别是source ,那么APT技术就是注解source级别的重要应用场景之一,其次该级别的应用场景是IDE可以根据注解来进行语法检查,例如:@IdRes ,@IntDef 等注解,详细用法可以去找度娘。
我们今天要学习的是注解另外一个应用场景(即Runtime保留级别)--反射的使用。
关于反射后面会有详细的文章来介绍,这里就不展开介绍了。
接下来我们就直接开撸:
我们还是接着使用上两篇中的自定义的注解,也可以到github上下载学习Demo
public class InjectUtils {
/*
* TODO 反射绑定view
*/
public static void injectAnnotation(final Activity activity) {
Class<? extends Activity> aClass = activity.getClass();
/*
* TODO 获取所有的自己的fields,不包括父类
*/
Field[] declaredFields = aClass.getDeclaredFields();
for (int i = 0; i < declaredFields.length; i++) {
//遍历所有的field
Field field = declaredFields[i];
if (field.isAnnotationPresent(BindView.class)) {
//找到使用BindView注解的属性,并取出对应的注解
BindView annotation = field.getAnnotation(BindView.class);
if (annotation != null) {
//获取到注解的参数值
int viewId = annotation.viewId();
//找到Id对应的视图
View view = activity.findViewById(viewId);
//设置field是可以访问的,否则则不能操作
field.setAccessible(true);
try {
//将找到的视图赋值给field
field.set(activity, view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/*
* TODO 获取该类所有的方法,不包括父类
*/
Method[] declaredMethods = aClass.getDeclaredMethods();
for (int i = 0; i < declaredMethods.length; i++) {
final Method declaredMethod = declaredMethods[i];
if (declaredMethod.isAnnotationPresent(ViewOnClick.class)) {
ViewOnClick annotation = declaredMethod.getAnnotation(ViewOnClick.class);
if (annotation != null) {
final int[] viewIds = annotation.viewId();
for (int i1 = 0; i1 < viewIds.length; i1++) {
final int finalI = i1;
activity.findViewById(viewIds[i1]).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//反射方法的调用使用invoke
declaredMethod.invoke(activity, viewIds[finalI]);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
}
}
}
}
}
上述代码几乎对每一句都进行了注释,大家可以看到,method 和Field的处理逻辑基础一致,所以下面处理method上的注解的注释可以对照上半部分。
特别说明:
- 由于每一次都会对所有的field ,或 method 进行遍历,这样会比较影响效率,这也是反射存在的一个问题
- 反射在注解中使用,那么注解必须是Runtime保留级别,笔者粗心使用上一篇中的SOURCE级别注解,未修改,则一直无法成功
- getDeclaredFields() 和getFields()两个方法的区别,前者是返回当前类的所有的成员变量,不包括父类;后者则返回当前类及其父类的(public)所有的成员变量(含static),那么如何区分父类的成员变量呢,可以调用superClass()的getDeclaredFields()方法获取。
同理:getDeclaredMethods()和getMethods()是一个道理
简单使用:
@BindView(viewId = R.id.inject_btn)
Button mInjectBtn;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_inject);
InjectUtils.injectAnnotation(this);
}
@ViewOnClick(viewId = R.id.inject_btn)
public void onClick(@IdRes int viewId) {
if (viewId == R.id.inject_btn) {
Toast.makeText(this, mInjectBtn.getText().toString(), Toast.LENGTH_SHORT).show();
}
}
其实和注解处理器的使用是一样的,只是InjectUtils.injectAnnotation(this);换成反射工具的调用了。
怎么样,反射来处理注解是不是很简单?以上是注解应用场景--运行时反射解析。那么注解保留级别是CLASS时的应用场景又是什么呢,我们下一篇来学习。