美文网首页
ButterKnife的实现原理解密之IOC思想

ButterKnife的实现原理解密之IOC思想

作者: 依玲之风 | 来源:发表于2019-10-13 17:49 被阅读0次

ButterKnife只要是写过Android的相信都不会陌生,它是一款控件注入、事件注如神器可以帮助开发者提高开发效率。
什么是IOC技术呢?说到IOC它的解释其实看上去并没有那么的容易懂,通俗一点讲IOC是原来由程序代码中主动获取的资源,转变由第三方获取并使原来的代码被动接收的方式,以达到解耦的效果,称为控制反转,想要更详细的要了解IOC的思想请到Spring的原码查看,这里只分享IOC思想的一部分。
那如何使用IOC思想实现Butterknife的控件注入的功能呢?下面将用代码来讲解实际中是如何实现的。
首先得有像Butterknife一样的自定义注解来标记一下哪一个控件是需要注入的,所以得定义一个自定义的注解

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

这个注解是在运行时生效的,同时是使用在成员变量上的,有了这个注解后就是可以写一个注入工具来实现控件的注入了,其实现原理是使用注解+反射来达到注入的效果,通过查找类中的所有成员找到带有自定义的注解拿到该控件的id值,然后反射系统中的findViewByid()的方法来达到注入的效果。

   /**
     *
     * @param targer
     */
    private static void injectFindViewById(Object targer) {
        Class<?> aClass = targer.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if(viewInject != null){
                int viewId = viewInject.value();
                try {
                    Method findViewByIdMethod = aClass.getMethod("findViewById",int.class);
                    View view = (View)findViewByIdMethod.invoke(targer,viewId);
                    field.setAccessible(true);
                    field.set(targer,view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

这个就是使用IOC技术来实现Butterknife的控件注入的功能,Butterknife还一个事件注入的功能这个功能的实现原理和控件注入差不多;接下就看一下事件的注入是如何做到的,这里的只实现一种事件注入的实现分析OnClick()的事件注入只要理解了一种的事件的注入其它的22种事件也是一样的。同样也是要定义一个自定义的注解来标记哪一个方法需要事件的注入。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@EventBase(listenerSetter = "setOnClickListener",listenerType = View.OnClickListener.class,callbackMethoed = "onClick")
public @interface OnClick {
    int[] value() default -1;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface EventBase {
    String listenerSetter();
    Class<?> listenerType();
    String callbackMethoed();
}

这里还需要再定义一个EventBase注解来记录这事件是要设置系统中的哪一个事件监听。有了注解后就是要来实现这个注入的功能了。

/**
    * 事件注入
    * @param targer
    */
   private static void injectEvent(Object targer) {
       Class<?> aClass = targer.getClass();
       Method[] declaredMethods = aClass.getDeclaredMethods();
       for (Method method : declaredMethods) {
           Annotation[] annotations = method.getAnnotations();
           for (Annotation annotation : annotations) {
               Class<?> annotationClass = annotation.annotationType();
               EventBase eventBase = annotationClass.getAnnotation(EventBase.class);
               if(eventBase == null){
                   continue;
               }
               String listenerSetter = eventBase.listenerSetter();
               Class<?> lisetenerType = eventBase.listenerType();
               String callBackMethod = eventBase.callbackMethoed();
               Method valueMethod = null;
               try{
                   valueMethod = annotationClass.getDeclaredMethod("value");
                   int[] viewId = (int[]) valueMethod.invoke(annotation);
                   for (int id : viewId) {
                       Method findViewByid = aClass.getMethod("findViewById",int.class);
                       View view = (View) findViewByid.invoke(targer,id);
                       if(view == null){
                           continue;
                       }
                       ListenerInvocationHandler invocationHandler = new ListenerInvocationHandler(targer,method);
                       Object proxy =  Proxy.newProxyInstance(lisetenerType.getClassLoader(),new Class[]{lisetenerType},invocationHandler);
                       Method onClickMethod = view.getClass().getMethod(listenerSetter,lisetenerType);
                       onClickMethod.invoke(view,proxy);
                   }
               }catch (Exception e){
                   e.printStackTrace();
               }
           }
       }
   }

其实现原理和控件注入是一样的都是通过反射系统中的方法来实现的,只是在事件的注入中要使用到动态代理才能把事件的回调到相应的方法上。

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 {
        return activityMethod.invoke(activity,args);
    }
}

以上就是使用IOC的思想来实现ButterKnife的控件的注入和事件的注入,从上述可以看到所有的注入都是使用反射来实现的从性能上来看其实并不太好,下面分析ButterKnife的实现过程。为了解决这个性能问题ButterKnife对IOC做了优化,从运行时改成了编译时来完成所有的注入功能。
要从编译时完成控件的注入和事件的注入那就使用注解处理器来完成这个事情。代码的实现下次再分享了。
项目代码链接:https://github.com/WayneQi/WayneDemo

相关文章

  • ButterKnife的实现原理解密之IOC思想

    ButterKnife只要是写过Android的相信都不会陌生,它是一款控件注入、事件注如神器可以帮助开发者提高开...

  • Spring IOC 实现原理

    Spring IOC 实现原理 IOC: Inversion of Control ,即 "控制反转" , 不是什...

  • dubbo之ExtensionLoader调用

    参考 Dubbo实现原理之基于SPI思想实现Dubbo内核

  • spring_IOC 实现原理

    IOC 实现原理 开发工作多年,spring源码没有特意去看过。理解实现原理,不如自己实现简易版的进一步理解IOC...

  • ButterKnife实现原理

    ButterKnife源码地址:https://github.com/JakeWharton/butterknif...

  • ButterKnife实现原理

    代码自动生成 使用代码自动生成,一是为了提高编码的效率,二十避免在运行期大量使用反射,通过在编译期利用反射生成辅助...

  • spring核心初探

    ioc实现原理 转载至:http://jiwenke.iteye.com/blog/493965 IOC基础 下面...

  • 理解Spring IOC

    TODO:未来按自己的思路和理解再整理 IOC概念 阅读:Spring的IOC原理 Spring中IOC实现 阅读...

  • Spring5

    IoC 控制反转Ioc(Inversion of Control),是一种设计思想,DI(依赖注入)是实现Ioc的...

  • 学习Spring IOC,看完这篇就够了

    学习Spring IOC,看完这篇就够了 说明 官网介绍了控制反转(IOC)原理的Spring框架实现。IoC也称...

网友评论

      本文标题:ButterKnife的实现原理解密之IOC思想

      本文链接:https://www.haomeiwen.com/subject/wmqkmctx.html