【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 )(二)

简介: 【IOC 控制反转】Android 事件依赖注入 ( 事件依赖注入代码示例 )(二)

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
     */
    @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);
    }
}



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);
        // 获取注解中的布局 ID
        int 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() {
                    @Override
                    public void onClick(View v) {
                    }
                });
                 */
                // 点击事件 View.setOnClickListener
                String listenerSetter = eventBase.listenerSetter();
                // 监听器类型 View.OnClickListener
                Class<?> 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;
    @Override
    protected 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();
    }
}


运行结果 :

image.png



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}

image.png


目录
相关文章
|
2月前
|
Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
2月前
|
存储 前端开发 测试技术
Android kotlin MVVM 架构简单示例入门
Android kotlin MVVM 架构简单示例入门
40 1
|
2月前
|
安全 Java 网络安全
Android远程连接和登录FTPS服务代码(commons.net库)
Android远程连接和登录FTPS服务代码(commons.net库)
34 1
|
2月前
|
Android开发
Android面试高频知识点(1) 图解 Android 事件分发机制
Android面试高频知识点(1) 图解 Android 事件分发机制
45 1
|
2月前
|
XML 前端开发 Android开发
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
Android面试高频知识点(1) 图解Android事件分发机制
|
2月前
|
Android开发
Android 事件分发机制详细解读
Android 事件分发机制详细解读
44 5
|
2月前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异:从代码到用户体验
【10月更文挑战第5天】在移动应用开发的广阔天地中,安卓和iOS两大平台各占半壁江山。它们在技术架构、开发环境及用户体验上有着根本的不同。本文通过比较这两种平台的开发过程,揭示背后的设计理念和技术选择如何影响最终产品。我们将深入探讨各自平台的代码示例,理解开发者面临的挑战,以及这些差异如何塑造用户的日常体验。
|
3月前
|
开发工具 Android开发 git
Android实战之组件化中如何进行版本控制和依赖管理
本文介绍了 Git Submodules 的功能及其在组件化开发中的应用。Submodules 允许将一个 Git 仓库作为另一个仓库的子目录,有助于保持模块独立、代码重用和版本控制。虽然存在一些缺点,如增加复杂性和初始化时间,但通过最佳实践可以有效利用其优势。
52 3
|
3月前
|
存储 Java Android开发
🔥Android开发大神揭秘:从菜鸟到高手,你的代码为何总是慢人一步?💻
在Android开发中,每位开发者都渴望应用响应迅速、体验流畅。然而,代码执行缓慢却是常见问题。本文将跟随一位大神的脚步,剖析三大典型案例:主线程阻塞导致卡顿、内存泄漏引发性能下降及不合理布局引起的渲染问题,并提供优化方案。通过学习这些技巧,你将能够显著提升应用性能,从新手蜕变为高手。
38 2
|
Android开发
Android Touch事件分发(源码分析)
Android一文让你轻松搞定Touch事件分发 源码分析 Activity事件分发机制 Activity.dispatchTouchEvent()源码 Activity.onTouchEvent()源码 Activity源码总结 ViewGroup事件分发机制 ViewGroup.dispatchTouchEvent()源码 ViewGroup.onInterceptTouchEvent()源码 ViewGroup.onTouchEvent()源码 ViweGroup源码总结 View的事件分发机制 View.dispatchTouchEvent()源码
187 0
Android Touch事件分发(源码分析)