注解反射&动态代理的封装
【摘要】 @TOC 1、自定义注解&动态代理的简单实现需求场景:通过自定义注解方式,实现按钮的点击效果,和长按效果我们先写两个按钮<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schem...
@TOC
1、自定义注解&动态代理的简单实现
需求场景:
通过自定义注解方式,实现按钮的点击效果,和长按效果
我们先写两个按钮
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮1"
tools:ignore="MissingConstraints,OnClick,UsingOnClickInXml" />
<Button
android:id="@+id/btn_2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="按钮2"
tools:ignore="MissingConstraints" />
</LinearLayout>
再写两个自定义的注解接口
OnClick:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnClick {
int[] value();
}
OnLongClick:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface OnLongClick {
int[] value();
}
在上面的两个接口中,分别定义了两个int 数组,用来接收存储我们传入的组件ID
再写一个接口注入方法的使用实现类,用来实现上面的 OnClick
和 OnLongClick
两个接口。
InjectUtils:
这里我们使用最原始的方式,通过getClass
获取 activity 上的class 对象,再通过getDeclaredMethods()
方法获取到 当前activity 中的所有方法。再遍历出来当前activity 中的所有注解,并找到自定义注解,
最后加入动态代理的方式,将事件反射出来。代码实现如下:
public class InjectUtils {
public static void injectEvent(Activity activity) {
// 通过getClass 获得activity 上的对象
Class<? extends Activity> aClass = activity.getClass();
// 获得activity 上的所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
// 获得方法上的所有注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
// 循环遍历出自定义的注解
if (annotation.annotationType() == OnClick.class) {
OnClick onClick = (OnClick) annotation;
int[] ids = onClick.value();
for (int id : ids) {
View viewById = activity.findViewById(id);
// 创建一个动态代理
Object listener = Proxy.newProxyInstance(activity.getClassLoader(),
new Class[]{View.OnClickListener.class},
new InvocationHandler() {
@Override
public Object invoke(Object o, Method m, Object[] objects) throws Throwable {
// return method.invoke(activity, args);
return method.invoke(activity, objects);
}
});
viewById.setOnClickListener((View.OnClickListener) listener);
}
} else if (annotation.annotationType() == OnLongClick.class) {
OnLongClick onLongClick = (OnLongClick) annotation;
int[] ids = onLongClick.value();
for (int id : ids) {
View viewById = activity.findViewById(id);
// 创建一个动态代理
Object listener = Proxy.newProxyInstance(activity.getClassLoader(),
new Class[]{View.OnLongClickListener.class},
new InvocationHandler() {
@Override
public Object invoke(Object o, Method m, Object[] objects) throws Throwable {
return method.invoke(activity, objects);
}
});
viewById.setOnLongClickListener((View.OnLongClickListener) listener);
}
}
}
}
}
}
上面代码中,我们使用动态代理proxy的方式,实现了自定义注解反射需求。但是这样会造成代码非常的臃肿,不易维护等很多情况,特别是在我们需要拓展的时候,每次拓展,都需要再重新写一份如下代码。
OnLongClick onLongClick = (OnLongClick) annotation;
int[] ids = onLongClick.value();
for (int id : ids) {
View viewById = activity.findViewById(id);
// 创建一个动态代理
Object listener = Proxy.newProxyInstance(activity.getClassLoader(),
new Class[]{View.OnLongClickListener.class},
new InvocationHandler() {
@Override
public Object invoke(Object o, Method m, Object[] objects) throws Throwable {
return method.invoke(activity, objects);
}
});
viewById.setOnLongClickListener((View.OnLongClickListener) listener);
}
2、代理优化封装
基于上面的代码,我们加以改造优化,新建一个 EventType 注解接口,代码如下:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface EventType {
Class listenerType();
String listenerSetter();
}
接下来,我们分别在 OnClick
和 OnLongClik
接口中,使用EventType
注解
@EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnLongClickListener")
最后我们去重新改造InjectUtils
类中的实现方法
具体的就不解释了,代码如下:
public class InjectUtils {
public static void injectEvent(Activity activity) {
// 通过getClass 获得activity 上的对象
Class<? extends Activity> aClass = activity.getClass();
// 获得activity 上的所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
// 获得方法上的所有注解
Annotation[] annotations = method.getAnnotations();
for (Annotation annotation : annotations) {
// 循环遍历出自定义的注解
Class<? extends Annotation> annotationType = annotation.annotationType();
// 拿到注解上的 EventType 注解
if (annotationType.isAnnotationPresent(EventType.class)){
EventType eventType = annotationType.getAnnotation(EventType.class);
// 获取定义的参数
Class listenerType = eventType.listenerType();
String listenerSetter = eventType.listenerSetter();
try {
Method value = annotationType.getDeclaredMethod("value");
int[] viewIds = (int[]) value.invoke(annotation);
Object proxy = Proxy.newProxyInstance(activity.getClassLoader(),
new Class[]{listenerType},
new InvocationHandler() {
@Override
public Object invoke(Object o, Method m, Object[] objects) throws Throwable {
return method.invoke(activity, objects);
}
});
for (int viewId: viewIds){
View view = activity.findViewById(viewId);
// 找到setOnClickListener(new OnClickListener)
Method setter = view.getClass().getMethod(listenerSetter, listenerType);
setter.invoke(view, proxy);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
经过上面的封装之后,我们以后不论想拓展什么的事件,只需要在类方法上面添加EventType
注解,并传入相关的事件就可以了
@EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnLongClickListener")
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)