IOC注入框架设计

2020-01-02  本文已影响0人  Coder_Sven

什么是IOC注入框架

IOC-控制反转(Inversion of Control,英文缩写为IoC)是一个重要的面向对象编程的法则来削减计算机程序的耦合问题,也是轻量级的Spring框架的核心。 控制反转一般分为两种类型,依赖注入(Dependency Injection,简称DI)和依赖查找(Dependency Lookup)。这段百度对IOC框架的解释,对于Java开发者来讲最著名的IOC框架莫过于Spring,而在我们的Android开发中,IOC的使用更为常见,比如大家经常使用的XUtil、butterKnife、EventBus、dagger、dagger2、otto等等,这些第三方库几乎都使用了IOC思想,比如使用ButterKnife对于view的注入减少了大量篇幅的findViewById操作,而注解注入的方式也显得更加优雅。

IOC思想

IOC是原来由程序代码中主动获取的资源,转变由第三方获取并使原来的代码被动接收的方式,以达到解耦的效果,称为控制反转

1577951887382.png

如何实现IOC

看看示例代码

@ContentView(R.layout.activity_main)
public class MainActivity extends BaseActivity {

    @ViewInject(R.id.app_text)
    private Button textView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
//        textView.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                Toast.makeText(MainActivity.this,"点击了",Toast.LENGTH_SHORT).show();
//            }
//        });
    }

    @OnClick({R.id.app_text,R.id.app_text1})
    public boolean click(View view){
        Toast.makeText(this,"---->"+textView,Toast.LENGTH_SHORT).show();
        return false;
    }

    @OnLongClick({R.id.app_text,R.id.app_text1})
    public boolean longClick(View view){
        Toast.makeText(this, "长按了", Toast.LENGTH_SHORT).show();
        return true;
    }
}
package com.highgreat.sven.ioc;

import android.app.Activity;
import android.os.Bundle;

public class BaseActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.inject(this);
    }
}
public class InjectUtils {

    public static void inject(Object object){
        injectLayout(object);
        injectView(object);
        injectClick(object);
    }
    ...
}

布局注入

@ContentView(R.layout.activity_main)

需要创建ContentView注解

package com.highgreat.sven.ioc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)//注解在运行时执行
@Target(ElementType.TYPE)//作用在类上面
public @interface ContentView {
    int value();
}

Java实现注解的执行逻辑

    /**
     * 布局注入
     * @param object
     */
    private static void injectLayout(Object object) {
        int layoutId = 0;
        Class<?> aClass = object.getClass();
        ContentView annotation = aClass.getAnnotation(ContentView.class);
        if(annotation != null){
            layoutId = annotation.value();
            try {
                //反射Activity的setContentView方法
                Method method = aClass.getMethod("setContentView", int.class);
                method.invoke(object,layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

这段代码首先通过object.getClass()拿到这个Class对象,再通过aClass.getAnnotation(ContentView.class);拿到类上的注解,取得layout的id,然后反射Activity的setcontentView(R.id.layout)方法,这样就实现了布局的注入

控件注入

定义注解文件

package com.highgreat.sven.ioc;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)//字段
public @interface ViewInject {
    int value();
}

java执行逻辑

    /**
     * 控件注入
     * @param object
     */
    private static void injectView(Object object) {
        Class<?> aClass = object.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if(viewInject != null){
                int value = viewInject.value();
                try {
                    Method method = aClass.getMethod("findViewById", int.class);
                    View view = (View) method.invoke(object,value);
                    field.setAccessible(true);//设置为可访问的权限。即将private修饰的字段变成可公共访问的
                    field.set(object,view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

这段代码首先通过aClass.getDeclaredFields()拿到类的所有字段属性,再通过ViewInject viewInject = field.getAnnotation(ViewInject.class);拿到字段属性的注解判断是否是需要注入的字段,再通过Method method = aClass.getMethod("findViewById", int.class);View view = (View) method.invoke(object,value);反射Activity类的findViewById方法获取到对应的view,setContentView方法没有返回值,而findViewById则相反,所以我们需要为属性(这里就是一些View)赋值,调用的是field.set(object,view)。

Android事件监听规律

事件的注入相比之前的布局和控件注入,难度和复杂度大大提高了。通过对Android中的事件监听代码的观察,我们得出如下三部曲:

定义事件监听规律的注解

package com.highgreat.sven.ioc;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)//作用在其他注解上
public @interface EventBase {

    //  setOnClickListener  订阅
    String listenerSetter();

    /**
     * 事件监听的类型
      */
    Class<?> listenerType();

    /**
     * 事件处理
     * @return
     */
    String callbackMethod();

}

这个注解是放在其他注解之上的,那么这个注解怎么使用呢,就以View的长按事件监听为例:

package com.highgreat.sven.ioc;

import android.view.View;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerType = View.OnLongClickListener.class
        ,listenerSetter =  "setOnLongClickListener",callbackMethod = "onLongClick")
public @interface OnLongClick {
    int[] value() default -1;
}

在这个注解上面调用了刚才定义的EventBase注解,根据传入的值大家似乎就什么都看明白了吧,没错这里传入了View.OnLongClickListener事件监听三部曲,因为在一个类中可能不止一个控件会设置长按事件监听,所以这里的返回值是数组。

事件注入逻辑

    /**
     * 事件注入
     * @param object
     */
    private static void injectClick(Object object) {
        Class<?> aClass = object.getClass();
        //得到类的所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        for (Method method : declaredMethods) {
            //得到方法上的所有注解(避免写死)
            Annotation[] annotations = method.getAnnotations();
            for (Annotation annotation : annotations) {
                Class<? extends Annotation> annotationType = annotation.annotationType();
                //创建一个事件类的超类,所有事件都是一个EventBase
                EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                //如果没有eventBase,则表示当前方法不是一个处理事件的方法
                if(eventBase==null) { 
                    continue;
                }
                // 用于确定是哪种事件(onClick还是onLongClick)以及由谁来处理
                //事件监听的类型
                Class<?> listenerType = eventBase.listenerType();//View.OnClickListener.class
                //订阅
                String listenerSetter = eventBase.listenerSetter();//setOnClickListener
                //事件处理   事件被触发之后,执行的回调方法的名称
                String callBackMethod=eventBase.callbackMethod();

                try {
                    //反射得到id,再根据id得到对应的View
                    Method valueMethod = annotationType.getDeclaredMethod("value");
                    int[] viewId = (int[])valueMethod.invoke(annotation);
                    for (int id : viewId) {
                        //根据id反射获取对应的view
                        Method findViewById=aClass.getMethod("findViewById",int.class);
                        View view = (View)findViewById.invoke(object, id);
                        if(view==null) {
                            continue;
                        }
                        //得到ID对应的VIEW以后
                        //开始在这个VIEW上执行监听  (使用动态代理)
                        //需要执行activity上的onClick方法
                        ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(object, method);
                        Object instance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class<?>[]{listenerType}, listenerInvocationHandler);
                       //setOnClickListener(new View.OnClickListener());
                        Method onclickMethod = view.getClass().getMethod(listenerSetter,listenerType);
                        onclickMethod.invoke(view,instance);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

使用动态代理的方式将method的callback方法Onlick交回给activity去处理

package com.highgreat.sven.ioc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class ListenerInvocationHandler implements InvocationHandler {

    private Object activity;
    private  Method activityMethod; //Onclick

    public ListenerInvocationHandler(Object activity, Method activityMethod) {
        this.activity = activity;
        this.activityMethod = activityMethod;
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在这里调用Activity下面注解了的click方法
        return activityMethod.invoke(activity,args);
    }
}

项目demo地址

上一篇下一篇

猜你喜欢

热点阅读