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(); } }
运行结果 :
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}