【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )
文章目錄
- 前言
- 一、創(chuàng)建 事件監(jiān)聽器 對(duì)應(yīng)的 動(dòng)態(tài)代理
- 二、動(dòng)態(tài)代理 數(shù)據(jù)準(zhǔn)備
- 三、動(dòng)態(tài)代理 調(diào)用處理程序
- 四、動(dòng)態(tài)代理 實(shí)例對(duì)象創(chuàng)建
前言
Android 依賴注入的核心就是通過(guò)反射獲取 類 / 方法 / 字段 上的注解 , 以及注解屬性 ; 在 Activity 基類中 , 獲取該注解 以及 注解屬性 , 進(jìn)行相關(guān)操作 ;
在博客 【IOC 控制反轉(zhuǎn)】Android 事件依賴注入 ( 事件三要素 | 修飾注解的注解 | 事件依賴注入步驟 ) 中 , 定義了 222 個(gè)注解 ,
- 第一個(gè)是方法上的注解 , 用于修飾方法 ;
- 第二個(gè)是修飾注解的注解 , 該注解用于配置注入的方法 ( 事件監(jiān)聽方法 | 監(jiān)聽器類型 | 監(jiān)聽器回調(diào)方法 ) ;
事件依賴注入比較復(fù)雜 , 涉及到動(dòng)態(tài)代理 , 本博客分析 【IOC 控制反轉(zhuǎn)】Android 事件依賴注入 ( 事件依賴注入代碼示例 ) 事件依賴注入的詳細(xì)步驟 ;
本博客的核心是 : 使用動(dòng)態(tài)代理 , 創(chuàng)建 View.OnClickListener 或 View.OnLongClickListener 或 View.onTouchListener 等接口的動(dòng)態(tài)代理類 ;
攔截相應(yīng)的 onClick , onLongClick , onTouch 方法 , 執(zhí)行自己的方法 , 其它方法正常執(zhí)行 ;
一、創(chuàng)建 事件監(jiān)聽器 對(duì)應(yīng)的 動(dòng)態(tài)代理
為組件設(shè)置的監(jiān)聽器可能是 View.OnClickListener 或 View.OnLongClickListener 或 View.onTouchListener 等監(jiān)聽器 , 因此使用 靜態(tài)代理 , 需要為每個(gè)監(jiān)聽器都要設(shè)置一個(gè)單獨(dú)的類 , 比較繁瑣 ;
這里使用動(dòng)態(tài)代理實(shí)現(xiàn)上述功能 ;
動(dòng)態(tài)代理是作用于接口上的 , 根據(jù)接口動(dòng)態(tài)創(chuàng)建該接口子類的代理對(duì)象 ;
原來(lái)是設(shè)置了一個(gè)匿名內(nèi)部類 , 這個(gè)匿名內(nèi)部類就是代理模式中的 被代理對(duì)象 ;
textView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}});現(xiàn)在使用 動(dòng)態(tài)代理 , 創(chuàng)建一個(gè) 代理對(duì)象 , 代理 上述 匿名內(nèi)部類 被代理對(duì)象 , 要在調(diào)用 onClick 方法時(shí) , 注入自己的業(yè)務(wù)邏輯 ;
該動(dòng)態(tài)代理中的元素梳理 :
- 目標(biāo)對(duì)象 ( 主題對(duì)象 ) : View.OnClickListener 接口 ;
- 被代理對(duì)象 : View.OnClickListener 接口匿名內(nèi)部類 ;
- 代理對(duì)象 : 使用 Proxy.newProxyInstance 方法 , 由 JVM 自動(dòng)生成字節(jié)碼類 就是代理對(duì)象 , 之后返回一個(gè)代理對(duì)象 的實(shí)例對(duì)象 ;
- 客戶端 : 框架開發(fā)者開發(fā)的 依賴注入 工具類 , 在該工具類中執(zhí)行動(dòng)態(tài)代理的調(diào)用操作 ;
二、動(dòng)態(tài)代理 數(shù)據(jù)準(zhǔn)備
執(zhí)行動(dòng)態(tài)代理前 , 首先要知道攔截接口方法 , 以及要注入的方法 ;
攔截到接口方法后 , 替換成自己注入的方法 , 就是調(diào)用自己的方法 ;
將二者封裝到 Map 集合中 , 方便在攔截后 , 調(diào)用 Map 的 get 方法 , 查看是否有要注入的方法 ;
// 攔截 callbackMethod 方法 , 執(zhí)行 method[i] 方法// 這個(gè) method[i] 方法就是在 MainActivity 中用戶自定義方法// 被 OnClick 注解修飾的方法// 將其封裝到 Map 集合中Map<String, Method> methodMap = new HashMap<>();methodMap.put(callbackMethod, methods[i]);三、動(dòng)態(tài)代理 調(diào)用處理程序
在該動(dòng)態(tài)代理中 , 首先要注入 Activity 和 上面準(zhǔn)備的 Map 集合 , Map 集合中封裝了 要攔截的接口方法 和 要注入的方法 ;
首先獲取被代理接口中的 回調(diào)的方法名稱, 該方法是 onClick 或者 onLongClick 或者 onTouch 等方法 ;
Method 方法在參數(shù)中有 , 直接調(diào)用 Method method 參數(shù)的 getName() 方法獲取接口名稱 ;
// 獲取回調(diào)的方法名稱, 該方法是 onClick 或者 onLongClick 或者 onTouch 等方法String name = method.getName();然后到 Map 集合中查找 , 是否要攔截該 接口方法 , 如果要攔截 , 肯定能從 Map 集合中獲取到要注入的方法 , 如果不需要攔截 , 獲取的結(jié)果是 null ;
// 獲取對(duì)應(yīng)的被調(diào)用方法Method method1 = methodMap.get(name);如果被調(diào)用的方法 需要被攔截 , 則能獲取到被攔截后替換的方法 , 執(zhí)行該注入的方法即可 ;
// 如果被調(diào)用的方法 需要被攔截 , 則能獲取到被攔截后替換的方法if (method1 != null) {// 執(zhí)行用戶 Activity 中的相應(yīng)方法return method1.invoke(activity, args);}如果不攔截該方法 , 則獲取的注入方法為 null , 直接返回該方法 , 注意調(diào)用 method.invoke(proxy, args) , 正常執(zhí)行該接口方法即可 ;
// 其它方法正常執(zhí)行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 方法 , 執(zhí)行 method[i] 方法* 這個(gè) method[i] 方法就是在 MainActivity 中用戶自定義方法* 被 OnClick 注解修飾的方法* 將其封裝到 Map 集合中*/private Map<String, Method> methodMap;public EventInvocationHandler(Activity activity, Map<String, Method> methodMap) {this.activity = activity;this.methodMap = methodMap;}/*** 攔截方法 , 并使用自己的方法替換* 如 : 發(fā)現(xiàn)是 onClick 方法 , 則替換成用戶自定義的方法 (被 @OnClick 注解修飾的方法)* @param proxy* @param method* @param args* @return* @throws Throwable*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 獲取回調(diào)的方法名稱, 該方法是 onClick 或者 onLongClick 或者 onTouch 等方法String name = method.getName();// 獲取對(duì)應(yīng)的被調(diào)用方法Method method1 = methodMap.get(name);// 如果被調(diào)用的方法 需要被攔截 , 則能獲取到被攔截后替換的方法if (method1 != null) {// 執(zhí)行用戶 Activity 中的相應(yīng)方法return method1.invoke(activity, args);}// 其它方法正常執(zhí)行return method.invoke(proxy, args);} }四、動(dòng)態(tài)代理 實(shí)例對(duì)象創(chuàng)建
調(diào)用 Proxy.newProxyInstance 方法 , 創(chuàng)建動(dòng)態(tài)代理的 實(shí)例對(duì)象 , 傳入到代理的接口數(shù)組 , 這個(gè)接口數(shù)組元素可以是 View.OnClickListener.class 或 View.OnLongClickListener.class 或 View.OnTouchListener.class 等字節(jié)碼類 ;
在調(diào)用處理程序中 , 攔截上述接口中的方法 , 并替換成自己的方法 , 也就是用戶在 MainActivity 中使用 @OnClick 注解修飾的方法 ;
// 獲取監(jiān)聽器 View.OnClickListener 接口的代理對(duì)象EventInvocationHandler eventInvocationHandler =new EventInvocationHandler(activity, methodMap);Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), // 類加載器new Class<?>[]{listenerType}, // 接口數(shù)組eventInvocationHandler); // 調(diào)用處理程序該動(dòng)態(tài)代理實(shí)例對(duì)象創(chuàng)建后 , 將其當(dāng)做 View.OnClickListener.class 或 View.OnLongClickListener.class 或 View.OnTouchListener.class 等字節(jié)碼類的實(shí)例對(duì)象使用即可 ;
總結(jié)
以上是生活随笔為你收集整理的【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【IOC 控制反转】Android 事件
- 下一篇: 【错误记录】Android Studio