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

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

文章目录

前言

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

二、动态代理 数据准备

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

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

前言

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


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


第一个是方法上的注解 , 用于修饰方法 ;

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

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



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

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






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


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


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


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


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


           

textView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                    }
                });


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



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


目标对象 ( 主题对象 ) : View.OnClickListener 接口 ;

被代理对象 : View.OnClickListener 接口匿名内部类 ;

 

new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                    }
                }


代理对象 : 使用 Proxy.newProxyInstance 方法 , 由 JVM 自动生成字节码类 就是代理对象 , 之后返回一个代理对象 的实例对象 ;

客户端 : 框架开发者开发的 依赖注入 工具类 , 在该工具类中执行动态代理的调用操作 ;





二、动态代理 数据准备


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


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


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


           

// 拦截 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() 方法获取接口名称 ;


   

// 获取回调的方法名称, 该方法是 onClick 或者 onLongClick 或者 onTouch 等方法
        String name = method.getName();



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


     

// 获取对应的被调用方法
        Method method1 = methodMap.get(name);



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


   

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



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


   

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



代码示例 :


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.class 或 View.OnLongClickListener.class 或 View.OnTouchListener.class 等字节码类 ;


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


                   

// 获取监听器 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 等字节码类的实例对象使用即可 ;


目录
相关文章
|
12天前
|
Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
15天前
|
Android开发
Android面试高频知识点(1) 图解 Android 事件分发机制
Android面试高频知识点(1) 图解 Android 事件分发机制
34 1
|
20天前
|
XML 前端开发 Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
28天前
|
Android开发
Android 事件分发机制详细解读
Android 事件分发机制详细解读
36 4
|
23天前
|
存储 大数据 数据库
Android经典面试题之Intent传递数据大小为什么限制是1M?
在 Android 中,使用 Intent 传递数据时存在约 1MB 的大小限制,这是由于 Binder 机制的事务缓冲区限制、Intent 的设计初衷以及内存消耗和性能问题所致。推荐使用文件存储、SharedPreferences、数据库存储或 ContentProvider 等方式传递大数据。
36 0
|
2月前
|
编译器 Android开发 开发者
带你了解Android Jetpack库中的依赖注入框架:Hilt
本文介绍了Hilt,这是Google为Android开发的依赖注入框架,基于Dagger构建,旨在简化依赖注入过程。Hilt通过自动化的组件和注解减少了DI的样板代码,提高了应用的可测试性和可维护性。文章详细讲解了Hilt的主要概念、基本用法及原理,帮助开发者更好地理解和应用Hilt。
71 8
|
2月前
|
API Android开发 iOS开发
掌握安卓与iOS应用开发中的依赖注入技术
本文探讨了在安卓和iOS应用开发中,如何有效利用依赖注入技术来提升代码的模块化、可测试性和可维护性。通过对比分析两种平台下依赖注入的实现方式与工具,本文旨在为开发者提供一套清晰、实用的依赖管理策略,助力打造高质量软件产品。
|
3月前
|
开发工具 Android开发
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
Android项目架构设计问题之组件A通知组件B某个事件的发生如何解决
39 0
|
数据采集 Android开发 索引
【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )(二)
【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )(二)
555 0
【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )(二)
|
数据采集 存储 传感器
【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )(一)
【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )(一)
271 0
【Android RTMP】音频数据采集编码 ( AAC 音频格式解析 | FLV 音频数据标签解析 | AAC 音频数据标签头 | 音频解码配置信息 )(一)