示例代码Github:https://github.com/345166018/AndroidIOC/tree/master/HxIOC
IOC 是原来由程序代码中主动获取的资源,转变由第三方获取并使原来的代码被动接收的方式,以达到解耦的效果,称为控制反转。
1 布局注入1.1 定义注解ContentVIEw@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)public @interface ContentVIEw { int value();}
1.2 将注解添加到MainActivity上@ContentVIEw(R.layout.activity_main)public class MainActivity extends BaseActivity { @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// setContentVIEw(R.layout.activity_main); }}
1.3 反射通过反射获取到注解上设置的值,并获取到setContentVIEw,然后将值传递到setContentVIEw方法中。
public class InjectUtils { public static voID inject(Object context) { injectLayout(context); } private static voID injectLayout(Object context) { int layouID = 0; Class<?> clazz = context.getClass(); ContentVIEw contentVIEw = clazz.getAnnotation(ContentVIEw.class); if (contentVIEw != null) { layouID = contentVIEw.value(); } try { Method method = context.getClass().getmethod("setContentVIEw", int.class); method.invoke(context, layouID); } catch (NoSuchMethodException e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } catch (InvocationTargetException e) { e.printstacktrace(); } }}
1.4 在BaseActivity中对注入工具进行初始化public class BaseActivity extends AppCompatActivity { @OverrIDe protected voID onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); InjectUtils.inject(this); }}
这样就完成了一个最简单的布局注入,在MainActivity我们没有使用setContentVIEw方法去设置布局文件,而是通过注解的形式,运行程序后app运行正常,说明达到了布局注入的目的。
2 控件注入2.1 定义注解VIEwInject
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.FIELD)public @interface VIEwInject { int value();}
2.2 添加注解在button上添加注解VIEwInject,这里没有使用findVIEwByID方法去获取button控件,并给button一个点击事件,查看是否真正获取到button控件。
@ContentVIEw(R.layout.activity_main)public class MainActivity extends BaseActivity { @VIEwInject(R.ID.btn_click) private button button; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);// setContentVIEw(R.layout.activity_main); Log.i("hongxue button string",button.toString()); button.setonClickListener(new VIEw.OnClickListener() { @OverrIDe public voID onClick(VIEw v) { Toast.makeText(MainActivity.this,"点击按钮",Toast.LENGTH_SHORT).show(); } }); }}
2.3 反射通过反射执行findVIEwByID
public class InjectUtils { public static voID inject(Object context) { injectLayout(context); injectVIEw(context); } private static voID injectVIEw(Object context) { Class<?> clazz = context.getClass(); FIEld[] fIElds = clazz.getDeclaredFIElds(); for (FIEld fIEld : fIElds){ VIEwInject vIEwInject = fIEld.getAnnotation(VIEwInject.class); if(vIEwInject != null){ int valueID = vIEwInject.value(); try { //反射执行findVIEwByID Method method = clazz.getmethod("findVIEwByID",int.class); VIEw vIEw = (VIEw) method.invoke(context,valueID); fIEld.setAccessible(true);//1 fIEld.set(context,vIEw);//2 } catch (NoSuchMethodException e) { e.printstacktrace(); } catch (illegalaccessexception e) { e.printstacktrace(); } catch (InvocationTargetException e) { e.printstacktrace(); } } } } ...}
注释1:
在java的反射使用中,如果字段是私有的,那么必须要对这个字段设置 。
值为 true 则指示反射的对象在使用时应该取消 Java 语言访问检查。值为 false 则指示反射的对象应该实施 Java 语言访问检查。
注释2:将反射findVIEwByID获取到的控件对象添加到对应的成员变量(fIEld)中。
初始化在布局注入中已经说到,在BaseActivity中添加InjectUtils.inject(this);
3 事件注入3.1 事件三要素
androID 所有的23事件
OnClickListener、OnLongClickListener …
点击事件的三要素
1 button 事件源
2 new VIEw.OnClickListener() 事件
3 onClick() 事件处理
进行订阅 setonClickListener
button.setonClickListener(new VIEw.OnClickListener() { @OverrIDe public voID onClick(VIEw v) { }}); button.setonLongClickListener(new VIEw.OnLongClickListener() { @OverrIDe public boolean onLongClick(VIEw v) { return false; }});
3.2 实现onClick事件3.2.1 定义注解@OnClick@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface OnClick { int[] value() default -1;}
3.2.2 添加注解到方法上在布局文件中添加了两个按钮,ID分别为btn_click2和btn_click3。点击不同的按钮显示不同的信息。
@OnClick({R.ID.btn_click2, R.ID.btn_click3}) public boolean click(VIEw vIEw) { if (vIEw.getID() == R.ID.btn_click2) { Log.i("hongxue", " MainActivity btn 2 click"); Toast.makeText(this, "点击了button2", Toast.LENGTH_SHORT).show(); } else if (vIEw.getID() == R.ID.btn_click3) { Log.i("hongxue", " MainActivity btn 3 click"); Toast.makeText(this, "点击了button3", Toast.LENGTH_SHORT).show(); } return false; }
3.2.3 实现事件注入在InjectUtils添加injectEvent方法。
/** * 事件注入 */ private static voID injectEvent(Object context) { Class<?> clazz = context.getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) { OnClick onClick = method.getAnnotation(OnClick.class); if (onClick == null) { continue; } String ListenerSetter = "setonClickListener"; Class<?> ListenerType = VIEw.OnClickListener.class; //String callBackMethod = "onClick"; try { int[] vIEwID = onClick.value(); for (int ID : vIEwID) { Method findVIEwByID = clazz.getmethod("findVIEwByID", int.class); VIEw vIEw = (VIEw) findVIEwByID.invoke(context, ID); if (vIEw == null) { continue; } ListenerInvocationHandler ListenerInvocationHandler = new ListenerInvocationHandler(context, method); Object proxy = Proxy.newProxyInstance(ListenerType.getClassLoader(), new Class[]{ListenerType}, ListenerInvocationHandler); Method onClickMethod = vIEw.getClass().getmethod(ListenerSetter, ListenerType); onClickMethod.invoke(vIEw, proxy); } } catch (Exception e) { e.printstacktrace(); } } }
将injectEvent方法添加到inject方法中。
public static voID inject(Object context) { injectLayout(context); injectVIEw(context); injectEvent(context); }
3.2.3 动态代理public class ListenerInvocationHandler implements InvocationHandler { private Object activity; private Method activityMethod; public ListenerInvocationHandler(Object activity, Method activityMethod) { this.activity = activity; this.activityMethod = activityMethod; } @OverrIDe public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //在这里去调用被注解了的click() return activityMethod.invoke(activity,args); }}
4 改造前面已经提到AndroID有23种事件,所以可以再创建一个注解,用于事件注解上,这样我们就不需要在injectEvent中去添加事件注解的三要素,而是可以在创建事件注解的时候再去设置三要素。
4.1 定义注解@EventBase@Retention(RetentionPolicy.RUNTIME)//该注解在另外一个注解上使用@Target(ElementType.ANNOTATION_TYPE)public @interface EventBase { // setonClickListener 订阅 String ListenerSetter(); // 事件以及他的类型 /** * 事件监听的类型 */ Class<?> ListenerType(); /** * 事件处理 */ String callbackMethod();}
4.2 改造注解@OnClick在@OnClick注解上使用使用@EventBase注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventBase(ListenerSetter = "setonClickListener" , ListenerType = VIEw.OnClickListener.class , callbackMethod = "onClick")public @interface OnClick { int[] value() default -1;}
4.3 改造事件注入方法 /** * 事件注入 */ private static voID injectEvent(Object context) { Class<?> clazz = context.getClass(); Method[] methods = clazz.getDeclaredMethods(); for (Method method : methods) {// OnClick onClick = method.getAnnotation(OnClick.class); //得到方法上的所有注解 Annotation[] annotations = method.getAnnotations();//1 for (Annotation annotation : annotations) {// annotation ===OnClick OnClick.class Class<?> annotionClass = annotation.annotationType();//2 EventBase eventBase = annotionClass.getAnnotation(EventBase.class);//3 //如果没有eventBase,则表示当前方法不是一个处理事件的方法 if (eventBase == null) { continue; } //开始获取事件处理的相关信息, // 用于确定是哪种事件(onClick还是onLongClick)以及由谁来处理 //订阅 String ListenerSetter = eventBase.ListenerSetter(); //事件(事件监听的类型) Class<?> ListenerType = eventBase.ListenerType(); //事件处理 事件被触发之后,执行的回调方法的名称 String callBackMethod = eventBase.callbackMethod();// textVIEw.setonClickListener(new VIEw.OnClickListener() {// @OverrIDe// public voID onClick(VIEw v) {//// }// });// int[] value1=OnClick.value();//这就写死了 Method valueMethod = null; try { //反射得到ID,再根据ID号得到对应的VIEW valueMethod = annotionClass.getDeclaredMethod("value"); int[] vIEwID = (int[]) valueMethod.invoke(annotation); for (int ID : vIEwID) { Method findVIEwByID = clazz.getmethod("findVIEwByID", int.class); VIEw vIEw = (VIEw) findVIEwByID.invoke(context, ID); if (vIEw == null) { continue; } //得到ID对应的VIEW以后 //开始在这个VIEW上执行监听 (使用动态代理) //需要执行activity上的onClick方法 //activity==context click==method ListenerInvocationHandler ListenerInvocationHandler = new ListenerInvocationHandler(context, method); //proxy======VIEw.OnClickListener()对象 Object proxy = Proxy.newProxyInstance(ListenerType.getClassLoader(), new Class[]{ListenerType}, ListenerInvocationHandler); //执行方法 setonClickListener,new VIEw.OnClickListener() Method onClickMethod = vIEw.getClass().getmethod(ListenerSetter, ListenerType); onClickMethod.invoke(vIEw, proxy); } } catch (Exception e) { e.printstacktrace(); } } } }
4.4 定义@OnLongClick注解再定义一个长按事件的注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)@EventBase(ListenerSetter = "setonLongClickListener", ListenerType = VIEw.OnLongClickListener.class, callbackMethod = "onLongClick")public @interface OnLongClick { int[] value() default -1;}
@OnLongClick({R.ID.btn_click2,R.ID.btn_click3}) public boolean longClick(VIEw vIEw) { if (vIEw.getID() == R.ID.btn_click2) { Log.i("hongxue", " MainActivity btn 2 longlick"); Toast.makeText(this, "长按了button2", Toast.LENGTH_SHORT).show(); } else if (vIEw.getID() == R.ID.btn_click3) { Log.i("hongxue", " MainActivity btn 3 longClick"); Toast.makeText(this, "长按了button3", Toast.LENGTH_SHORT).show(); } return true;//1 }
注释1:return true 可以拦截点击事件
示例代码Github:https://github.com/345166018/AndroidIOC/tree/master/HxIOC
总结以上是内存溢出为你收集整理的Android IOC控制反转简单实现全部内容,希望文章能够帮你解决Android IOC控制反转简单实现所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)