IOC注解实现流程,实现butterknife的注解功能

本文介绍Android开发中使用IOC注解框架实现布局、控件及事件注入的原理与方法,包括自定义注解、反射机制的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目前市场上比较火的Android注解框架有butterknife,dagger等,IOC的核心是解耦,修改耦合对象时不影响另外一个对象,降低模块之间的关联。IOC注解的优缺点:
1.代码量少,加速开发
2.产生性能消耗

开发过程中,经常会使用findviewById的方式去实例化一个控件的id,比如获取一个Button的id
Button btn = findviewById(R.id.btn);
如果你学会了注解的方式,就可以这样实现
@InjectView(R.id.btn)
Button btn
本文主要从三个方面讲解如何实现IOC注解功能:

1、布局注入

原理:通过放射,获得类→布局注解→注解的值→获取指定方法→执行方法,具体的步骤如下:

  1. 新建一个布局注解Annotation,代码如下
   /**
    * @author zhengjun
    * @desc   布局的注解
    * @create at 2019/3/22 16:26
    */
     @Target(ElementType.TYPE) //该注解作用在类上
     @Retention(RetentionPolicy.RUNTIME) //运行时通过反射获取该注解的值
     public @interface ContentView {
        int value();
     }
  1. 实现注入过程,获得类→布局注解→注解的值→获取指定方法→执行方法
    //注入布局layout
    private static void injectLayout(Activity activity) {
        //获取类
        Class<? extends Activity> aClass = activity.getClass();
        //获取类的注解
        ContentView contentView = aClass.getAnnotation(ContentView.class);
        if (contentView != null) {
            //获取当前类的布局R.layout.activity_main
            int layoutId = contentView.value();
            //1.第一种方法注解,直接调用setContentView方法
            //activity.setContentView(layoutId);
            try {
                //2.第二种方法注解,通过反射机制
                Method method = aClass.getMethod("setContentView", int.class);
                method.invoke(activity, layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
  1. 在activity中使用此注解
    用@ContentView(R.layout.activity_main)的方式代替setContentView()方法

2、控件注入

原理:通过放射,获得类→获取所有属性→遍历属性→每个属性的注解→每个注解的→获取指定方法→执行方法→设置访问私有→赋值

  1. 新建一个控件注解Annotation,代码如下
   /**
    * @author zhengjun
   * @desc   view的注解
   * @create at 2019/3/22 16:54
   */
   @Target(ElementType.FIELD) //该注解作用在属性上
   @Retention(RetentionPolicy.RUNTIME)
   public @interface InjectView {
       int value();
   }
  1. 实现注入过程,获得类→获取所有属性→遍历属性→每个属性的注解→每个注解的→获取指定方法→执行方法→设置访问私有→赋值
   //控件注入
    private static void injectViews(Activity activity) {
        //获取类
        Class<? extends Activity> aClass = activity.getClass();
        //获取类所有的属性
        Field[] fields = aClass.getDeclaredFields();
        //遍历属性
        for (Field filed : fields) {
            //获取属性上的注解
            InjectView injectView = filed.getAnnotation(InjectView.class);
            if (injectView != null) { //并不是每个属性都有这个注解,所有要做非空判断
                //获取这个属性注解的值R.id.btn
                int viewId = injectView.value();
                try {
                    //获取方法
                    Method method = aClass.getMethod("findViewById", int.class);
                    //执行方法,得到view对象
                    Object view = method.invoke(activity, viewId);
                    //设置改属性可以访问,哪怕是private类型
                    filed.setAccessible(true);
                    //属性的值赋给控件,在当前的activity
                    filed.set(activity, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
  1. 在activity中使用此注解
    用@InjectView(R.id.btn)的方式代替findViewById()方法

3、事件注入

原理:通过放射,获得类→获取所有方法→遍历方法→每个方法的注解→遍历所有注解→获取指定的注解(类型,判空)→获取最终注解的三大成员→获得最终注解的值→遍历注解的值→赋值控件→控件获得指定方法→执行方法

  1. 新建一个布局注解Annotation,代码如下
    /**
     * @author zhengjun
     * @desc
     * @create at 2019/3/25 09:53
     */
    @Target(ElementType.METHOD) //该注解作用在方法上
    @Retention(RetentionPolicy.RUNTIME)
    @EventBase(listenerSet = "setOnClickListener", listenerType = View.OnClickListener.class, callBackListener =     "onClick")
    public @interface OnClick {
        int[] value();
    }
  1. 新建一个作用在注解上的注解
    /**
     * @author zhengjun
     * @desc
     * @create at 2019/3/25 10:34
     */
    @Target(ElementType.ANNOTATION_TYPE) //元注解 作用在注解之上
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EventBase {
         //事件的三个重要成员
        
         //1.setOnxxxListener方法名
         String listenerSet();

         //2.监听的对象 View.OnxxxListener()
         Class<?> listenerType();

         //3.回调方法
         String callBackListener();
}
  1. 新建一个类实现InvocationHandler实现方法的拦截
    /**
 * @author zhengjun
 * @desc
 * @create at 2019/3/25 11:06
 */
public class ListenerInvocationHandler implements InvocationHandler {
    //需要拦截mainactivity中的某些方法
    private Object target;

    //拦截的键值对
    private HashMap<String, Method> methodHashMap = new HashMap<>();

    public ListenerInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (target != null) {
            String name = method.getName();
            //重新赋值,拦截了onClick方法
            method = methodHashMap.get(name);
            //如果找到了 就执行自定义的onClick方法
            if (method != null) {
                return method.invoke(target, args);
            }
        }
        return null;
    }

    /**
     * @desc 拦截方法
     * methodName 要拦截的方法 onClick
     * method     自定义要执行的方法
     * @author zhengjun
     * @created at 2019/3/25 11:12
     */
    public void addMethod(String methodName, Method method) {
        methodHashMap.put(methodName, method);
    }
}
  1. 实现注入过程,获得类→获取所有方法→遍历方法→每个方法的注解→遍历所有注解→获取指定的注解(类型,判空)→获取最终注解的三大成员→获得最终注解的值→遍历注解的值→赋值控件→控件获得指定方法→执行方法
   //事件注入
    private static void injectEvents(Activity activity) {
        //获取当前类
        Class<? extends Activity> aClass = activity.getClass();
        //获取所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        //遍历方法
        for (Method method : declaredMethods) {
            //获取方法注解,可能一个方法会有多个注解
            Annotation[] annotations = method.getAnnotations();
            //遍历注解
            for (Annotation annotation : annotations) {
                //获取注解类型
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                    //事件三个重要成员
                    String listenerSet = eventBase.listenerSet();
                    Class<?> listenerType = eventBase.listenerType();
                    String callBackListener = eventBase.callBackListener();
                    try {
                        //通过annotationType获取onClick注解的value值
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        //R.id.tv  R.id.btn
                        int[] viewIds = (int[]) valueMethod.invoke(annotation);
                        ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(activity);
                        invocationHandler.addMethod(callBackListener, method);
                        Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, invocationHandler);
                        for (int viewId : viewIds) {
                            //得到当前控件view
                            View view = activity.findViewById(viewId);
                            Method setter = view.getClass().getMethod(listenerSet, listenerType);
                            setter.invoke(view, listener);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
  1. 在activity中使用此注解
    用@OnClick({R.id.btn,R.id.tv})的方式代替setOnclickListener()方法

最后,贴出整个注解过程的代码,全部封装到InjectManager类中,可以在activity的基类中调用InjectManager.init(this)方法进行注入,代码如下:

 package me.zhengjun.injectview;
import android.app.Activity;
import android.view.View;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import me.zhengjun.injectview.annotation.ContentView;
import me.zhengjun.injectview.annotation.EventBase;
import me.zhengjun.injectview.annotation.InjectView;
import me.zhengjun.injectview.listener.ListenerInvocationHandler;

/**
 * @author zhengjun
 * @desc   注入的管理类
 * @create at 2019/3/22 16:26
 */
public class InjectManager {
    public static void inject(Activity activity) {
        //布局注入
        injectLayout(activity);

        //控件注入
        injectViews(activity);

        //事件注入
        injectEvents(activity);
    }

    //注入布局layout
    private static void injectLayout(Activity activity) {
        //获取类
        Class<? extends Activity> aClass = activity.getClass();
        //获取类的注解
        ContentView contentView = aClass.getAnnotation(ContentView.class);
        if (contentView != null) {
            //获取当前类的布局R.layout.activity_main
            int layoutId = contentView.value();
            //1.第一种方法注解,直接调用setContentView方法
            //activity.setContentView(layoutId);
            try {
                //2.第二种方法注解,通过反射机制
                Method method = aClass.getMethod("setContentView", int.class);
                method.invoke(activity, layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    //控件注入
    private static void injectViews(Activity activity) {
        //获取类
        Class<? extends Activity> aClass = activity.getClass();
        //获取类所有的属性
        Field[] fields = aClass.getDeclaredFields();
        //遍历属性
        for (Field filed : fields) {
            //获取属性上的注解
            InjectView injectView = filed.getAnnotation(InjectView.class);
            if (injectView != null) { //并不是每个属性都有这个注解,所有要做非空判断
                //获取这个属性注解的值R.id.btn
                int viewId = injectView.value();
                try {
                    //获取方法
                    Method method = aClass.getMethod("findViewById", int.class);
                    //执行方法,得到view对象
                    Object view = method.invoke(activity, viewId);
                    //设置改属性可以访问,哪怕是private类型
                    filed.setAccessible(true);
                    //属性的值赋给控件,在当前的activity
                    filed.set(activity, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }
        }
    }

    //事件注入
    private static void injectEvents(Activity activity) {
        //获取当前类
        Class<? extends Activity> aClass = activity.getClass();
        //获取所有方法
        Method[] declaredMethods = aClass.getDeclaredMethods();
        //遍历方法
        for (Method method : declaredMethods) {
            //获取方法注解,可能一个方法会有多个注解
            Annotation[] annotations = method.getAnnotations();
            //遍历注解
            for (Annotation annotation : annotations) {
                //获取注解类型
                Class<? extends Annotation> annotationType = annotation.annotationType();
                if (annotationType != null) {
                    EventBase eventBase = annotationType.getAnnotation(EventBase.class);
                    //时间三个重要成员
                    String listenerSet = eventBase.listenerSet();
                    Class<?> listenerType = eventBase.listenerType();
                    String callBackListener = eventBase.callBackListener();
                    try {
                        //通过annotationType获取onClick注解的value值
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        //R.id.tv  R.id.btn
                        int[] viewIds = (int[]) valueMethod.invoke(annotation);
                        ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(activity);
                        invocationHandler.addMethod(callBackListener, method);
                        Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, invocationHandler);
                        for (int viewId : viewIds) {
                            //得到当前控件view
                            View view = activity.findViewById(viewId);
                            Method setter = view.getClass().getMethod(listenerSet, listenerType);
                            setter.invoke(view, listener);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值