【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 )
文章目录
- 总结
- 一、Android 事件依赖注入示例
- 1、创建依赖注入库
- 2、声明注解
- (1)、修饰注解的注解
- (2)、修饰方法的注解
- 3、Activity 基类
- 4、动态代理类调用处理程序
- 5、依赖注入工具类
- 6、客户端 Activity
- 二、博客源码
总结
Android 依赖注入的核心就是通过反射获取 类 / 方法 / 字段 上的注解 , 以及注解属性 ; 在 Activity 基类中 , 获取该注解 以及 注解属性 , 进行相关操作 ;
一、Android 事件依赖注入示例
1、创建依赖注入库
首先在 Android 应用中 , 创建一个 " 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 {/*** 设置事件监听的方法* @return*/String listenerSetter();/*** 设置监听器类型* @return*/Class<?> listenerType();/*** 事件触发后的回调方法* @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 类型数组
}
@Target(ElementType.METHOD)
表示该注解作用于方法上 ;
@Retention(RetentionPolicy.RUNTIME)
注解保留到运行时 , Java 源码时期 RetentionPolicy.SOURCE
-> Class 字节码时期 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 参数是 MainActivity 子类对象InjectUtils.inject(this);}
}
4、动态代理类调用处理程序
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*/@Overridepublic 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);}
}
5、依赖注入工具类
将上一篇博客 【IOC 控制反转】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 字节码对象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 字节码对象Class<? extends Activity> clazz = activity.getClass();// 获取类的属性字段Field[] fields = clazz.getDeclaredFields();// 循环遍历类的属性字段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 注解 , 执行下一次循环continue;}// 获取注入的视图组件int viewId = bindView.value();// 根据组件 id 获取对应组件对象View view = activity.findViewById(viewId);try {// 通过反射设置 Activity 的对应属性字段的值field.set(activity, view);} catch (IllegalAccessException e) {e.printStackTrace();}}}/*** 注入事件*/private static void injectEvents(Activity activity) {// 获取 Class 字节码对象Class<? extends Activity> clazz = activity.getClass();// 获取所有方法Method[] methods = clazz.getDeclaredMethods();// 循环遍历类的方法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 注解 , 执行下一次循环continue;}// 如果获取到了 EventBase 注解 , 则开始获取事件注入的三要素/*通过反射执行下面的方法textView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {}});*/// 点击事件 View.setOnClickListenerString listenerSetter = eventBase.listenerSetter();// 监听器类型 View.OnClickListenerClass<?> listenerType = eventBase.listenerType();// 事件触发回调方法 public void onClick(View v)String callbackMethod = eventBase.callbackMethod();// 拦截 callbackMethod 方法 , 执行 method[i] 方法// 这个 method[i] 方法就是在 MainActivity 中用户自定义方法// 被 OnClick 注解修饰的方法// 将其封装到 Map 集合中Map<String, Method> methodMap = new HashMap<>();methodMap.put(callbackMethod, methods[i]);// 通过反射注入事件 , 设置组件的点击方法// 通过反射获取注解中的属性// int[] value(); // 接收 int 类型数组try {// 通过反射获取 OnClick 注解的 int[] value() 方法Method valueMethod = annotationType.getDeclaredMethod("value");// 调用 value() 方法 , 获取视图组件 ID 数组int[] viewIds = (int[]) valueMethod.invoke(annotations[j]);// 遍历 ID 数组for (int k = 0; k < viewIds.length; k ++) {// 获取组件实例对象View view = activity.findViewById(viewIds[k]);if (view == null) {continue;}// 获取 View 视图组件的 listenerSetter 对应方法// 这里是 View.setOnClickListener// 参数一是方法名称 , 参数二是方法参数类型Method listenerSetterMethod =view.getClass().getMethod(listenerSetter, listenerType);// 获取监听器 View.OnClickListener 接口的代理对象EventInvocationHandler eventInvocationHandler =new EventInvocationHandler(activity, methodMap);Object proxy = Proxy.newProxyInstance(listenerType.getClassLoader(), // 类加载器new Class<?>[]{listenerType}, // 接口数组eventInvocationHandler); // 调用处理程序// 执行 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 启动时 , 调用 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();}
}
运行结果 :
Logcat 打印结果 :
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
【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 )相关推荐
- Spring→简介核心作用范围、框架、接口编程、IOC控制反转、单元测试、Bean容器、注入、作用域、生命周期、自动装配注入、自动扫描@注解
官网 https://spring.io/ 文档 https://spring.io/projects/spring-framework Spring框架 框架 接口 IOC控制反转 单元测试 Bea ...
- 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 创建 事件监听器 对应的 动态代理 | 动态代理的数据准备 | 创建调用处理程序 | 创建动态代理实例对象 )
文章目录 前言 一.创建 事件监听器 对应的 动态代理 二.动态代理 数据准备 三.动态代理 调用处理程序 四.动态代理 实例对象创建 前言 Android 依赖注入的核心就是通过反射获取 类 / 方 ...
- 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 获取要注入事件的 View 对象 | 通过反射获取 View 组件的事件设置方法 )
文章目录 前言 一.获取要注入事件的 View 对象 二.通过反射获取 View 组件的事件设置方法并执行 前言 Android 依赖注入的核心就是通过反射获取 类 / 方法 / 字段 上的注解 , ...
- 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入具体的操作细节 | 获取 Activity 中的所有方法 | 获取方法上的注解 | 获取注解上的注解 | 通过注解属性获取事件信息 )
文章目录 前言 一.获取 Activity 中的所有方法 二.获取方法上的注解 三.获取注解上的注解 四.通过注解属性获取相关事件信息 前言 Android 依赖注入的核心就是通过反射获取 类 / 方 ...
- 【IOC 控制反转】Android 视图依赖注入 ( 视图依赖注入步骤 | 视图依赖注入代码示例 )
文章目录 总结 一.Android 视图依赖注入步骤 二.Android 布局依赖注入示例 1.创建依赖注入库 2.声明注解 3.Activity 基类 4.依赖注入工具类 5.客户端 Activit ...
- 【IOC 控制反转】Android 布局依赖注入 ( 布局依赖注入步骤 | 布局依赖注入代码示例 )
文章目录 总结 一.Android 布局依赖注入步骤 二.Android 布局依赖注入示例 1.创建依赖注入库 2.声明注解 3.Activity 基类 4.依赖注入工具类 5.客户端 Activit ...
- 【IOC 控制反转】IOC 简介 ( 依赖注入的两种实现方式 | 编译期注入 | 运行期注入 )
文章目录 一.IOC 简介 二.依赖注入的两种实现方式 一.IOC 简介 IOC 是 Inversion Of Control 的缩写 , 控制反转 ; 其最主要的作用是 降低代码的耦合度 , 最常见 ...
- IOC控制反转(依赖注入思想)
IOC控制反转(依赖注入思想) 获取map 类型的 request session application 的方法: 实现RequestAware,SessionAware,ApplicationAw ...
- 【Java从0到架构师】Spring - IoC 控制反转、DI 依赖注入
IoC 控制反转.DI 依赖注入 Spring 简介 Spring 基本使用 - IoC 容器 依赖注入 (Dependency Injection) 基于 setter 的注入 - 自定义对象 be ...
最新文章
- 机器学习中的梯度下降( Gradient Descent)算法
- java future用法_Java中的多线程知识点
- 影像组学视频学习笔记(43)-标准差、标准误及95%置信区间CI、Li‘s have a solution and plan.
- Oracle归档日志删除
- Dijkstra算法.
- 利用STM32 的串口来发送和接收数据实验
- 【OpenYurt 深度解析】边缘网关缓存能力的优雅实现
- A - Greed CodeForces - 892A(水题)
- Topshelf创建Windows服务
- linux文本文件和win文本文件的格式互换
- HDU - 2122 Ice_cream’s world III
- 在Java中安全地将long转换为int
- visual studio 2013连接Oracle 11g并获取数据:(一:环境搭建)
- Julia: reduce 、mapreduce、filter
- twisted系列教程二–缓慢的诗
- 10个比较不错的 JavaScript 库
- Spring 学习记录6 BeanFactory(2)
- 文档大小超出上传限制怎么办_有道翻译和翻译狗,哪个更适合翻译文档?
- Kubernetes快速部署
- java 时间类 joda_Java Joda-Time 处理时间工具类(JDK1.7以上)