注解反射&动态代理的封装


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

再写一个接口注入方法的使用实现类,用来实现上面的 OnClickOnLongClick 两个接口。

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();
}

接下来,我们分别在 OnClickOnLongClik 接口中,使用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")
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

半身风雪

感谢打赏,你的鼓励,是我创作的

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值