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

举报
半身风雪 发表于 2022/06/30 10:11:04 2022/06/30
【摘要】 @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

再写一个接口注入方法的使用实现类,用来实现上面的 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")
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。