【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 )
文章目錄
- 總結(jié)
- 一、Android 事件依賴注入示例
- 1、創(chuàng)建依賴注入庫
- 2、聲明注解
- (1)、修飾注解的注解
- (2)、修飾方法的注解
- 3、Activity 基類
- 4、動態(tài)代理類調(diào)用處理程序
- 5、依賴注入工具類
- 6、客戶端 Activity
- 二、博客源碼
總結(jié)
Android 依賴注入的核心就是通過反射獲取 類 / 方法 / 字段 上的注解 , 以及注解屬性 ; 在 Activity 基類中 , 獲取該注解 以及 注解屬性 , 進行相關(guān)操作 ;
一、Android 事件依賴注入示例
1、創(chuàng)建依賴注入庫
首先在 Android 應用中 , 創(chuàng)建一個 " Android Library " ,
設置主應用依賴該 Android 依賴庫 :
dependencies {implementation project(path: ':ioc_lib') }2、聲明注解
(1)、修飾注解的注解
package kim.hsl.ioc_lib;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** 自定義注解* 用于注解上的注解* 用于依賴注入視圖*/ @Target(ElementType.ANNOTATION_TYPE) // 該注解作用于注解上 @Retention(RetentionPolicy.RUNTIME) // 注解保留到運行時 public @interface EventBase {/*** 設置事件監(jiān)聽的方法* @return*/String listenerSetter();/*** 設置監(jiān)聽器類型* @return*/Class<?> listenerType();/*** 事件觸發(fā)后的回調(diào)方法* @return*/String callbackMethod(); }
(2)、修飾方法的注解
package kim.hsl.ioc_lib;import android.view.View;import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/*** 自定義注解* 用于依賴注入視圖*/ @Target(ElementType.METHOD) // 該注解作用于方法上 @Retention(RetentionPolicy.RUNTIME) // 注解保留到運行時 @EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,callbackMethod = "onClick") public @interface OnClick {int[] value(); // 接收 int 類型數(shù)組 }
@Target(ElementType.METHOD) 表示該注解作用于方法上 ;
@Retention(RetentionPolicy.RUNTIME) 注解保留到運行時 , Java 源碼時期 RetentionPolicy.SOURCE -> Class 字節(jié)碼時期 RetentionPolicy.CLASS -> JVM 運行時時期 RetentionPolicy.RUNTIME ;
int value() 表示該注解接收一個 int 類型的值 ;
3、Activity 基類
package kim.hsl.ioc_lib;import android.app.Activity; import android.os.Bundle;import androidx.annotation.Nullable;public class BaseActivity extends Activity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 在此處注入布局// 此處傳入的 Activity 參數(shù)是 MainActivity 子類對象InjectUtils.inject(this);} }
4、動態(tài)代理類調(diào)用處理程序
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] 方法* 這個 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();// 獲取對應的被調(diào)用方法Method method1 = methodMap.get(name);// 如果被調(diào)用的方法 需要被攔截 , 則能獲取到被攔截后替換的方法if (method1 != null) {// 執(zhí)行用戶 Activity 中的相應方法return method1.invoke(activity, args);}// 其它方法正常執(zhí)行return method.invoke(proxy, args);} }
5、依賴注入工具類
將上一篇博客 【IOC 控制反轉(zhuǎn)】Android 布局依賴注入 ( 布局依賴注入步驟 | 布局依賴注入代碼示例 ) 中的布局注入 , 抽到 injectLayout 方法中 ; 將注入視圖組件定義在 injectViews 方法中 ;
package kim.hsl.ioc_lib;import android.app.Activity; import android.icu.lang.UProperty; import android.view.View;import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.HashMap; import java.util.Map;public class InjectUtils {/*** 為 Activity 注入布局* @param activity 該 Activity 是繼承了 BaseActivity 的 MainActivity 實例對象*/public static void inject(Activity activity) {// 注入布局文件injectLayout(activity);// 注入視圖組件injectViews(activity);// 注入事件injectEvents(activity);}/*** 注入布局文件*/private static void injectLayout(Activity activity) {// 獲取 Class 字節(jié)碼對象Class<? extends Activity> clazz = activity.getClass();// 反射獲取類上的注解ContentView contentView = clazz.getAnnotation(ContentView.class);// 獲取注解中的布局 IDint layoutId = contentView.value();// 為 Activity 設置顯示的布局activity.setContentView(layoutId);}/*** 注入視圖組件*/private static void injectViews(Activity activity) {// 獲取 Class 字節(jié)碼對象Class<? extends Activity> clazz = activity.getClass();// 獲取類的屬性字段Field[] fields = clazz.getDeclaredFields();// 循環(huán)遍歷類的屬性字段for (int i = 0; i < fields.length; i ++) {// 獲取字段Field field = fields[i];// 屬性有可能是私有的, 這里設置可訪問性field.setAccessible(true);// 獲取字段上的注解, @BindView 注解// 注意不是所有的屬性字段都有 @BindView 注解BindView bindView = field.getAnnotation(BindView.class);if (bindView == null) {// 如果沒有獲取到 BindView 注解 , 執(zhí)行下一次循環(huán)continue;}// 獲取注入的視圖組件int viewId = bindView.value();// 根據(jù)組件 id 獲取對應組件對象View view = activity.findViewById(viewId);try {// 通過反射設置 Activity 的對應屬性字段的值field.set(activity, view);} catch (IllegalAccessException e) {e.printStackTrace();}}}/*** 注入事件*/private static void injectEvents(Activity activity) {// 獲取 Class 字節(jié)碼對象Class<? extends Activity> clazz = activity.getClass();// 獲取所有方法Method[] methods = clazz.getDeclaredMethods();// 循環(huán)遍歷類的方法for (int i = 0; i < methods.length; i ++) {// 獲取方法的所有注解Annotation[] annotations = methods[i].getDeclaredAnnotations();// 遍歷所有的注解for (int j = 0; j < annotations.length; j ++) {// 獲取注解類型Class<? extends Annotation> annotationType = annotations[j].annotationType();// 獲取 @EventBase 注解EventBase eventBase = annotationType.getAnnotation(EventBase.class);if (eventBase == null) {// 如果沒有獲取到 EventBase 注解 , 執(zhí)行下一次循環(huán)continue;}// 如果獲取到了 EventBase 注解 , 則開始獲取事件注入的三要素/*通過反射執(zhí)行下面的方法textView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}});*/// 點擊事件 View.setOnClickListenerString listenerSetter = eventBase.listenerSetter();// 監(jiān)聽器類型 View.OnClickListenerClass<?> listenerType = eventBase.listenerType();// 事件觸發(fā)回調(diào)方法 public void onClick(View v)String callbackMethod = eventBase.callbackMethod();// 攔截 callbackMethod 方法 , 執(zhí)行 method[i] 方法// 這個 method[i] 方法就是在 MainActivity 中用戶自定義方法// 被 OnClick 注解修飾的方法// 將其封裝到 Map 集合中Map<String, Method> methodMap = new HashMap<>();methodMap.put(callbackMethod, methods[i]);// 通過反射注入事件 , 設置組件的點擊方法// 通過反射獲取注解中的屬性// int[] value(); // 接收 int 類型數(shù)組try {// 通過反射獲取 OnClick 注解的 int[] value() 方法Method valueMethod = annotationType.getDeclaredMethod("value");// 調(diào)用 value() 方法 , 獲取視圖組件 ID 數(shù)組int[] viewIds = (int[]) valueMethod.invoke(annotations[j]);// 遍歷 ID 數(shù)組for (int k = 0; k < viewIds.length; k ++) {// 獲取組件實例對象View view = activity.findViewById(viewIds[k]);if (view == null) {continue;}// 獲取 View 視圖組件的 listenerSetter 對應方法// 這里是 View.setOnClickListener// 參數(shù)一是方法名稱 , 參數(shù)二是方法參數(shù)類型Method listenerSetterMethod =view.getClass().getMethod(listenerSetter, listenerType);// 獲取監(jiān)聽器 View.OnClickListener 接口的代理對象EventInvocationHandler eventInvocationHandler =new EventInvocationHandler(activity, methodMap);Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), // 類加載器new Class<?>[]{listenerType}, // 接口數(shù)組eventInvocationHandler); // 調(diào)用處理程序// 執(zhí)行 View 的 setOnClickListener 方法, 為其設置點擊事件listenerSetterMethod.invoke(view, proxy);}} catch (NoSuchMethodException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}}}} }6、客戶端 Activity
package kim.hsl.ioc_demo;import android.util.Log; import android.view.View; import android.widget.TextView; import android.widget.Toast;import kim.hsl.ioc_lib.BaseActivity; import kim.hsl.ioc_lib.BindView; import kim.hsl.ioc_lib.ContentView; import kim.hsl.ioc_lib.OnClick;/*** 當該 MainActivity 啟動時 , 調(diào)用 BaseActivity 的 onCreate 方法* 在 BaseActivity 的 onCreate 方法中注入布局*/ @ContentView(R.layout.activity_main) // 布局注入 public class MainActivity extends BaseActivity {/*** 視圖注入*/@BindView(R.id.textView)private TextView textView;@Overrideprotected void onResume() {super.onResume();// 驗證 textView 是否注入成功Log.i("MainActivity", "textView : " + textView);}@OnClick({R.id.textView}) // 事件注入public void onClick(View view) {Toast.makeText(this, "點擊 TextView 組件", Toast.LENGTH_LONG).show();} }
運行結(jié)果 :
Logcat 打印結(jié)果 :
2021-09-22 08:25:31.759 29044-29044/kim.hsl.ioc_demo I/MainActivity: textView : android.widget.TextView{a988249 VFED..C.. ........ 440,840-640,891 #7f08017e app:id/textView}二、博客源碼
GitHub : https://github.com/han1202012/IOC_Demo
CSDN : https://download.csdn.net/download/han1202012/24031128
總結(jié)
以上是生活随笔為你收集整理的【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 )的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【IOC 控制反转】Android 事件
- 下一篇: 【IOC 控制反转】Android 事件