Ioc注入框架 注入布局 注入控件 动态代理注入事件
来源:互联网 发布:nginx centos yum 编辑:程序博客网 时间:2024/06/02 04:57
IOC控制反转注入框架
很早之前我们用过Xutils框架 里面有通过注解来使用findViewById 之前我们只是使用。
这样的框架我们要自己实现一遍
主要分为三个部分
1. 注入布局 (利用注解)
2. 注入控件
3. 注入事件 (利用动态代理注入事件)
注入布局
定义注入布局时注解
package com.jiang.iocxutil.annotion;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by jiang on 2017/6/17. * 注入布局 * * ElementType.TYPE 表示类的注解 * RetentionPolicy.RUNTIME 运行时注解 一般我们编写的都是基于运行时的注解 */@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface ContentView{ int value();}
在基类中初始化注入
package com.jiang.iocxutil;import android.os.Bundle;import android.os.PersistableBundle;import android.support.annotation.Nullable;import android.support.v7.app.AppCompatActivity;/** * Created by jiang on 2017/6/17. */public class BaseActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); InjectUtils.inject(this); }}
注入布局相关代码
/** * 注入布局 * @param context */ public static void injectLayout(Context context) { Class<?> clazz = context.getClass(); ContentView contentView = clazz.getAnnotation(ContentView.class); if (contentView != null) { int layoutId = contentView.value(); //setContentView try { Method method = clazz.getMethod("setContentView", int.class); method.setAccessible(true); method.invoke(context, layoutId); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } }
在MainActiviy里面编写注入布局
package com.jiang.iocxutil;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.util.Log;import android.view.View;import android.widget.TextView;import android.widget.Toast;import com.jiang.iocxutil.annotion.ContentView;import com.jiang.iocxutil.annotion.OnClick;import com.jiang.iocxutil.annotion.OnLongClick;import com.jiang.iocxutil.annotion.ViewInject;/** * IOC控制反转框架 * */@ContentView(value = R.layout.activity_main)public class MainActivity extends BaseActivity { private static final String TAG = "MainActivity";// @Override// protected void onCreate(Bundle savedInstanceState) {// super.onCreate(savedInstanceState);// setContentView(R.layout.activity_main);// } @ViewInject(R.id.text_ioc) TextView textView; @Override protected void onResume() { super.onResume(); Log.d(TAG, "textView ==" + textView.hashCode());// textView.setOnClickListener(new View.OnClickListener() {// @Override// public void onClick(View v) {//// }// });// textView.setOnLongClickListener(new View.OnLongClickListener() {// @Override// public boolean onLongClick(View v) {// return false;// }// }); }// @OnClick(R.id.text_ioc)// public void click(View view){// toast("测试点击");// } @OnLongClick(R.id.text_ioc) public boolean click(View view){ toast("测试点击"); return false; } public void toast(String string) { Toast.makeText(this, string, Toast.LENGTH_SHORT).show(); }}
这样就可以通过注入布局来代替展示SetContentView()方法
注入控件
定义注解
package com.jiang.iocxutil.annotion;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by jiang on 2017/6/17. * 注入控件注解 */@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject { int value();}
2.利用反射找到findViewById来注入控件
/** * 依赖注入控件 * @param context */ public static void injectView(Context context) { Class<?> aClass = context.getClass(); // 拿到成员变量 数组 Field[] fields = aClass.getDeclaredFields(); // 遍历所有的属性 for (Field field: fields) { ViewInject viewInject = field.getAnnotation(ViewInject.class); if (viewInject != null) { int valueID = viewInject.value(); try { Method method = aClass.getMethod("findViewById", int.class); View view = (View) method.invoke(context, valueID); field.setAccessible(true); field.set(context, view); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } }
- 在MainActivity中我们使用 测试一下注入控件是否可用
@ViewInject(R.id.text_ioc) TextView textView; @Override protected void onResume() { super.onResume(); Log.d(TAG, "textView ==" + textView.hashCode()); }
注入事件(这个比较复杂也是难点)
为了事件的扩展性 我们要拿到事件的三个要素
1.事件的注册
2.事件的类型
3.事件的回调方法
package com.jiang.iocxutil.annotion;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by jiang on 2017/6/17. * 注解的注解 事件的三要素 */@Target(ElementType.ANNOTATION_TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface EventBase { /** * 监听事件的方法 * @return String */ String listenerSetter(); /** * 事件的类型 * @return Class */ Class<?> listenerType(); /** * 事件被触发后的回调方法 * @return */ String callBackMethod();}
我们先写点击事件
package com.jiang.iocxutil.annotion;import android.app.Dialog;import android.view.View;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by jiang on 2017/6/17. * 以EventBase做注解 * * 目前需要View.OnClickListener.class * 为了扩展可能还需要Dialog.OnClickListener.class */@EventBase(listenerSetter = "setOnClickListener" , listenerType = View.OnClickListener.class , callBackMethod = "onClick")@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface OnClick { int[] value();}
然后通过反射和动态代理去执行回调
/** * 注入事件 * * public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。 * public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法, * 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。 * @param context */ private static void injectEvent(Context context) { Class<?> clazz = context.getClass(); Method[] methods = clazz.getMethods(); for (Method method: methods) { /** * 扩展性 */ Annotation[] anns = method.getAnnotations(); // 循环拿到方法类型注解 for (Annotation ann: anns) { // 拿到注解的类 先拿到OnlickC注解 Class<? extends Annotation> anntionType = ann.annotationType(); //然后再拿到注解的注解EvenBase EventBase eventBase = anntionType.getAnnotation(EventBase.class); if (eventBase == null) { continue; } //拿到事件的三要素 // 设置事件 拿到 setOnclickListener String listenerSetter = eventBase.listenerSetter(); // 事件类型 拿到 View.OnClickListener.class Class<?> listenerType = eventBase.listenerType(); // 回调方法 拿到 callBackMethod onClick String callBackMethod = eventBase.callBackMethod(); // 下一步 通过反射 给View 设置 // 继续反射拿到 注解里面的id Map<String, Method> methodMap = new HashMap<>(); // 得到当前callBackMethod 对应 Onclick method -- clikText methodMap.put(callBackMethod, method); try { // Method declaredMethod = anntionType.getMethod("value"); // 注解上的方法 找到Id数组 int[] valuesId = (int[]) declaredMethod.invoke(ann); for (int viewId: valuesId) { Method findViewById = clazz.getMethod("findViewById", int.class); View view = (View) findViewById.invoke(context, viewId); // if (view == null) { continue; } //上面的事件三个要素全部拿到了 View的Class setOnClickListener 对应 view的setOnClickListener Method setOnClickListener = view.getClass().getMethod(listenerSetter, listenerType); ListenerInvocationHandler handler = new ListenerInvocationHandler(context, methodMap); // 拿到动态代理 Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{ listenerType }, handler); // 将我们的方法执行 setOnClickListener.invoke(view, proxyInstance); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } /** * 如何给其设置一个监听 onClick又不在InjectUtils回调 而是在MainActivity里面回调 * 动态代理 今天的需求是要给按钮设置一个监听 我要执行的一个回调监听不能卸载InjectUtils里面 * 而是想回调 MainActivity */
动态代理类的设计
package com.jiang.iocxutil;import android.content.Context;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.util.Map;/** * Created by jiang on 2017/6/17. */public class ListenerInvocationHandler implements InvocationHandler { private Context context; private Map<String, Method> methodMap; public ListenerInvocationHandler(Context context, Map<String, Method> methodMap) { this.context = context; this.methodMap = methodMap; } /** * 处理代理对象的代理方法 * 我们要代理谁 MainActivity OnClickListener * 持有一个真正的对象引用就是MainActivity * @param proxy * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String name = method.getName(); Method mtd = methodMap.get(name); if (mtd == null) { //不需要代理 return method.invoke(proxy, args); } else { //真正的代理方法 return mtd.invoke(context, args); } }}
然后再MainActivity里面测试注册事件
@OnClick(R.id.text_ioc) public void click(View view){ toast("测试点击"); }
此时我们的注册事件就已经完工。
我们看一下其扩展性
然后写长按点击事件 首先定义注解类似于点击事件
package com.jiang.iocxutil.annotion;import android.view.View;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/** * Created by jiang on 2017/6/17. * 长按事件的注解 */@EventBase(listenerSetter = "setOnLongClickListener" , listenerType = View.OnLongClickListener.class , callBackMethod = "onLongClick")@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface OnLongClick { int[] value();}
在Manactivity里面 添加
@OnLongClick(R.id.text_ioc) public boolean longLlick(View view){ toast("测试点击"); return false; }
长按点击也就可以成功了
注入工具类 包含注入布局 注入控件 和 注入事件
package com.jiang.iocxutil;import android.content.Context;import android.view.View;import com.jiang.iocxutil.annotion.ContentView;import com.jiang.iocxutil.annotion.EventBase;import com.jiang.iocxutil.annotion.ViewInject;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;/** * Created by jiang on 2017/6/17. * 依赖注入工具类、 * 分别为注入布局 * 注入控件 * 注入事件 */public class InjectUtils { /** * 初始化注入 * @param context */ public static void inject(Context context) { injectLayout(context); injectView(context); injectEvent(context); } /** * 依赖注入控件 * @param context */ public static void injectView(Context context) { Class<?> aClass = context.getClass(); // 拿到成员变量 数组 Field[] fields = aClass.getDeclaredFields(); // 遍历所有的属性 for (Field field: fields) { ViewInject viewInject = field.getAnnotation(ViewInject.class); if (viewInject != null) { int valueID = viewInject.value(); try { Method method = aClass.getMethod("findViewById", int.class); View view = (View) method.invoke(context, valueID); field.setAccessible(true); field.set(context, view); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } /** * 注入布局 * @param context */ public static void injectLayout(Context context) { Class<?> clazz = context.getClass(); ContentView contentView = clazz.getAnnotation(ContentView.class); if (contentView != null) { int layoutId = contentView.value(); //setContentView try { Method method = clazz.getMethod("setContentView", int.class); method.setAccessible(true); method.invoke(context, layoutId); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } /** * 注入事件 * * public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。 * public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法, * 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。 * @param context */ private static void injectEvent(Context context) { Class<?> clazz = context.getClass(); Method[] methods = clazz.getMethods(); for (Method method: methods) { /** * 扩展性 */ Annotation[] anns = method.getAnnotations(); // 循环拿到方法类型注解 for (Annotation ann: anns) { // 拿到注解的类 先拿到OnlickC注解 Class<? extends Annotation> anntionType = ann.annotationType(); //然后再拿到注解的注解EvenBase EventBase eventBase = anntionType.getAnnotation(EventBase.class); if (eventBase == null) { continue; } //拿到事件的三要素 // 设置事件 拿到 setOnclickListener String listenerSetter = eventBase.listenerSetter(); // 事件类型 拿到 View.OnClickListener.class Class<?> listenerType = eventBase.listenerType(); // 回调方法 拿到 callBackMethod onClick String callBackMethod = eventBase.callBackMethod(); // 下一步 通过反射 给View 设置 // 继续反射拿到 注解里面的id Map<String, Method> methodMap = new HashMap<>(); // 得到当前callBackMethod 对应 Onclick method -- clikText methodMap.put(callBackMethod, method); try { // Method declaredMethod = anntionType.getMethod("value"); // 注解上的方法 找到Id数组 int[] valuesId = (int[]) declaredMethod.invoke(ann); for (int viewId: valuesId) { Method findViewById = clazz.getMethod("findViewById", int.class); View view = (View) findViewById.invoke(context, viewId); // if (view == null) { continue; } //上面的事件三个要素全部拿到了 View的Class setOnClickListener 对应 view的setOnClickListener Method setOnClickListener = view.getClass().getMethod(listenerSetter, listenerType); ListenerInvocationHandler handler = new ListenerInvocationHandler(context, methodMap); // 拿到动态代理 Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{ listenerType }, handler); // 将我们的方法执行 setOnClickListener.invoke(view, proxyInstance); } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } /** * 如何给其设置一个监听 onClick又不在InjectUtils回调 而是在MainActivity里面回调 * 动态代理 今天的需求是要给按钮设置一个监听 我要执行的一个回调监听不能卸载InjectUtils里面 * 而是想回调 MainActivity */}
匆匆编写 细节还需大家验证 有疑问
邮箱 zhangdanjiang_java@163.com
GitBub地址 https://github.com/JiangGeJavaAndroid/IocXutil
阅读全文
0 0
- Ioc注入框架 注入布局 注入控件 动态代理注入事件
- 注入
- 注入
- 注入
- spring 注入 动态注入
- Android IOC 依赖注入框架
- Android架构设计05-基于IOC的布局,控件,事件注入
- IOC架构设计之事件注入
- Spring容器框架、spring ioc、两种注入方法set注入与构造器注入
- Spring框架学习【IoC容器依赖注入】
- Spring框架——IOC依赖注入
- IoC框架(依赖注入 DI)
- spring框架详解(一)--IOC与注入
- Ioc注入的艺术
- IoC 注入类型
- spring ioc 2.5 注入
- IoC 注入类型
- IOC与依赖注入
- JAVA变量
- 人脑杂念与电脑进程
- 用c++实现一个二叉排序树
- 第一次开通,记录一下
- Linux基础操作
- Ioc注入框架 注入布局 注入控件 动态代理注入事件
- python写算法题:leetcode: 24. Swap Nodes in Pairs
- 正则表达式小结
- 深入探究HashMap的实现原理
- 网络端口的小记
- Python数据类型Number数字
- Python3.X_HTMLTestRunner亲测可用,附说明
- MyFlag Step14: 打卡功能界面的初步
- OpenGL