Android IOC控制反转简单实现

Android IOC控制反转简单实现,第1张

概述文章目录1布局注入1.1定义注解ContentView1.2将注解添加到MainActivity上1.3反射1.4在BaseActivity中对注入工具进行初始化2控件注入2.1定义注解ViewInject2.2添加注解2.3反射3事件注入3.1事件三要素3.2实现onClick事件3.2.1定义注解@OnClick3.2.2添加注解到

文章目录1 布局注入1.1 定义注解ContentView1.2 将注解添加到MainActivity上1.3 反射1.4 在BaseActivity中对注入工具进行初始化2 控件注入2.1 定义注解ViewInject2.2 添加注解2.3 反射3 事件注入3.1 事件三要素3.2 实现onClick事件3.2.1 定义注解@OnClick3.2.2 添加注解到方法上3.2.3 实现事件注入3.2.3 动态代理4 改造4.1 定义注解@EventBase4.2 改造注解@OnClick4.3 改造事件注入方法4.4 定义@OnLongClick注解

示例代码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控制反转简单实现所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1105013.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-28
下一篇 2022-05-28

发表评论

登录后才能评论

评论列表(0条)

保存