前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )

【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )

作者头像
韩曙亮
发布2023-03-29 17:28:24
2.4K0
发布2023-03-29 17:28:24
举报
文章被收录于专栏:韩曙亮的移动开发专栏

文章目

前言

Android 依赖注入的核心就是通过反射获取 类 / 方法 / 字段 上的注解 , 以及注解属性 ; 在 Activity 基类中 , 获取该注解 以及 注解属性 , 进行相关操作 ;

在博客 【IOC 控制反转】Android 事件依赖注入 ( 事件三要素 | 修饰注解的注解 | 事件依赖注入步骤 ) 中 , 定义了

2

个注解 ,

  • 第一个是方法上的注解 , 用于修饰方法 ;
  • 第二个是修饰注解的注解 , 该注解用于配置注入的方法 ( 事件监听方法 | 监听器类型 | 监听器回调方法 ) ;

事件依赖注入比较复杂 , 涉及到动态代理 , 本博客分析 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 ) 事件依赖注入的详细步骤 ;

本博客的核心是 : 使用动态代理 , 创建 View.OnClickListener View.OnLongClickListener View.onTouchListener 等接口的动态代理类 ;

拦截相应的 onClick , onLongClick , onTouch 方法 , 执行自己的方法 , 其它方法正常执行 ;

一、创建 事件监听器 对应的 动态代理


为组件设置的监听器可能是 View.OnClickListenerView.OnLongClickListenerView.onTouchListener 等监听器 , 因此使用 静态代理 , 需要为每个监听器都要设置一个单独的类 , 比较繁琐 ;

这里使用动态代理实现上述功能 ;

动态代理是作用于接口上的 , 根据接口动态创建该接口子类的代理对象 ;

原来是设置了一个匿名内部类 , 这个匿名内部类就是代理模式中的 被代理对象 ;

代码语言:javascript
复制
                textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                    }
                });

现在使用 动态代理 , 创建一个 代理对象 , 代理 上述 匿名内部类 被代理对象 , 要在调用 onClick 方法时 , 注入自己的业务逻辑 ;

该动态代理中的元素梳理 :

  • 目标对象 ( 主题对象 ) : View.OnClickListener 接口 ;
  • 被代理对象 : View.OnClickListener 接口匿名内部类 ;
代码语言:javascript
复制
				new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                    }
                }
  • 代理对象 : 使用 Proxy.newProxyInstance 方法 , 由 JVM 自动生成字节码类 就是代理对象 , 之后返回一个代理对象 的实例对象 ;
  • 客户端 : 框架开发者开发的 依赖注入 工具类 , 在该工具类中执行动态代理的调用操作 ;

二、动态代理 数据准备


执行动态代理前 , 首先要知道拦截接口方法 , 以及要注入的方法 ;

拦截到接口方法后 , 替换成自己注入的方法 , 就是调用自己的方法 ;

将二者封装到 Map 集合中 , 方便在拦截后 , 调用 Mapget 方法 , 查看是否有要注入的方法 ;

代码语言:javascript
复制
                // 拦截 callbackMethod 方法 , 执行 method[i] 方法
                //      这个 method[i] 方法就是在 MainActivity 中用户自定义方法
                //      被 OnClick 注解修饰的方法
                //      将其封装到 Map 集合中
                Map<String, Method> methodMap = new HashMap<>();
                methodMap.put(callbackMethod, methods[i]);

三、动态代理 调用处理程序


在该动态代理中 , 首先要注入 Activity 和 上面准备的 Map 集合 , Map 集合中封装了 要拦截的接口方法 和 要注入的方法 ;

首先获取被代理接口中的 回调的方法名称, 该方法是 onClick 或者 onLongClick 或者 onTouch 等方法 ;

Method 方法在参数中有 , 直接调用 Method method 参数的 getName() 方法获取接口名称 ;

代码语言:javascript
复制
        // 获取回调的方法名称, 该方法是 onClick 或者 onLongClick 或者 onTouch 等方法
        String name = method.getName();

然后到 Map 集合中查找 , 是否要拦截该 接口方法 , 如果要拦截 , 肯定能从 Map 集合中获取到要注入的方法 , 如果不需要拦截 , 获取的结果是 null ;

代码语言:javascript
复制
        // 获取对应的被调用方法
        Method method1 = methodMap.get(name);

如果被调用的方法 需要被拦截 , 则能获取到被拦截后替换的方法 , 执行该注入的方法即可 ;

代码语言:javascript
复制
        // 如果被调用的方法 需要被拦截 , 则能获取到被拦截后替换的方法
        if (method1 != null) {
            // 执行用户 Activity 中的相应方法
            return method1.invoke(activity, args);
        }

如果不拦截该方法 , 则获取的注入方法为 null , 直接返回该方法 , 注意调用 method.invoke(proxy, args) , 正常执行该接口方法即可 ;

代码语言:javascript
复制
        // 其它方法正常执行
        return method.invoke(proxy, args);

代码示例 :

代码语言:javascript
复制
package kim.hsl.ioc_lib;

import android.app.Activity;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

public class EventInvocationHandler implements InvocationHandler {
    /**
     * 客户端 Activity
     */
    private Activity activity;

    /**
     * 拦截 callbackMethod 方法 , 执行 method[i] 方法
     *      这个 method[i] 方法就是在 MainActivity 中用户自定义方法
     *      被 OnClick 注解修饰的方法
     *      将其封装到 Map 集合中
     */
    private Map<String, Method> methodMap;

    public EventInvocationHandler(Activity activity, Map<String, Method> methodMap) {
        this.activity = activity;
        this.methodMap = methodMap;
    }

    /**
     * 拦截方法 , 并使用自己的方法替换
     *      如 : 发现是 onClick 方法 , 则替换成用户自定义的方法 (被 @OnClick 注解修饰的方法)
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 获取回调的方法名称, 该方法是 onClick 或者 onLongClick 或者 onTouch 等方法
        String name = method.getName();
        // 获取对应的被调用方法
        Method method1 = methodMap.get(name);

        // 如果被调用的方法 需要被拦截 , 则能获取到被拦截后替换的方法
        if (method1 != null) {
            // 执行用户 Activity 中的相应方法
            return method1.invoke(activity, args);
        }

        // 其它方法正常执行
        return method.invoke(proxy, args);
    }
}

四、动态代理 实例对象创建

调用 Proxy.newProxyInstance 方法 , 创建动态代理的 实例对象 , 传入到代理的接口数组 , 这个接口数组元素可以是 View.OnClickListener.classView.OnLongClickListener.classView.OnTouchListener.class 等字节码类 ;

在调用处理程序中 , 拦截上述接口中的方法 , 并替换成自己的方法 , 也就是用户在 MainActivity 中使用 @OnClick 注解修饰的方法 ;

代码语言:javascript
复制
                        // 获取监听器 View.OnClickListener 接口的代理对象
                        EventInvocationHandler eventInvocationHandler =
                                new EventInvocationHandler(activity, methodMap);
                        Object proxy = Proxy.newProxyInstance(
                                listenerType.getClassLoader(),  // 类加载器
                                new Class<?>[]{listenerType},   // 接口数组
                                eventInvocationHandler);        // 调用处理程序

该动态代理实例对象创建后 , 将其当做 View.OnClickListener.class View.OnLongClickListener.class View.OnTouchListener.class 等字节码类的实例对象使用即可 ;

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021-09-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 文章目
  • 前言
  • 一、创建 事件监听器 对应的 动态代理
  • 二、动态代理 数据准备
  • 三、动态代理 调用处理程序
  • 四、动态代理 实例对象创建
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档