美文网首页
IOC之运行时注入-实现Activity的布局注入+控件注入+事

IOC之运行时注入-实现Activity的布局注入+控件注入+事

作者: milovetingting | 来源:发表于2020-05-08 11:26 被阅读0次

    个人博客

    http://www.milovetingting.cn

    IOC之运行时注入-实现Activity的布局注入+控件注入+事件绑定

    前言

    本文主要介绍基于IOC的设计原则,实现以下功能:

    • 布局注入

    • 控件注入

    • 事件注入

    其实这些功能,在之前也有零散地介绍过,这里再做一个统一的整理。

    这里暂时不考虑运行时反射的效率问题,只是展示一种实现方案。

    IOC的定义

    IOC,即Inversion of Control,意为控制反转,是面向对象编程中的一种设计原则,可以用来降低代码间的耦合。最常见的方式是依赖注入(Dependence Injection,简称DI)。通过IOC,对象在创建时,由外界来控制,而不是内部直接控制。

    布局注入

    平时,我们在Activity中,可能会通过在onCreate方法中调用setContentView的方法,给Activity绑定布局。而基于IOC,则可以通过注解来实现:

    注解的定义

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface LayoutInject {
        /**
         * 布局id
         *
         * @return
         */
        @LayoutRes int value();
    }
    

    注解的使用

    @LayoutInject(R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            InjectUtil.inject(this);
        }
    }
    

    在MainActivity上加上前面定义的LayoutInject注解,然后在onCreate中调用注入的方法InjectUtil.inject(this)

    Inject方法

    /**
         * 注入
         *
         * @param target
         */
        public static void inject(Object target) {
            if (target == null) {
                return;
            }
            injectLayout(target);
        }
    
        /**
         * 布局注入
         *
         * @param target 需要注入的组件
         */
        private static void injectLayout(Object target) {
            Class<?> clazz = target.getClass();
            boolean annotationPresent = clazz.isAnnotationPresent(LayoutInject.class);
            if (!annotationPresent) {
                return;
            }
            LayoutInject annotation = clazz.getAnnotation(LayoutInject.class);
            int layoutId = annotation.value();
            try {
                Method method = clazz.getMethod("setContentView", int.class);
                method.invoke(target, layoutId);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    控件注入

    注解的定义

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ViewInject {
        /**
         * 控件id
         *
         * @return
         */
        @IdRes int value();
    }
    

    注解的使用

     @ViewInject(R.id.btn1)
        Button btn1;
    
     @ViewInject(R.id.btn2)
        Button btn2;
    

    Inject方法

    /**
         * 注入
         *
         * @param target
         */
        public static void inject(Object target) {
            if (target == null) {
                return;
            }
            injectView(target);
        }
    
    /**
         * 控件注入
         *
         * @param target 需要注入的组件
         */
        private static void injectView(Object target) {
            Class<?> clazz = target.getClass();
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                boolean annotationPresent = field.isAnnotationPresent(ViewInject.class);
                if (!annotationPresent) {
                    continue;
                }
                ViewInject annotation = field.getAnnotation(ViewInject.class);
                int viewId = annotation.value();
                try {
                    Method method = clazz.getMethod("findViewById", int.class);
                    View view = (View) method.invoke(target, viewId);
                    field.setAccessible(true);
                    field.set(target, view);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    

    事件注入

    注解的定义

    事件类型的注解

    @Target(ElementType.ANNOTATION_TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Event {
    
        /**
         * 设置listener的方法,如:setOnClickListener
         *
         * @return
         */
        String listenerSetter();
    
        /**
         * 事件,如:new View.OnClickListener()
         *
         * @return
         */
        Class<?> listenerType();
    }
    

    点击事件的注解

    @Event(listenerSetter = "setOnClickListener", listenerType = View.OnClickListener.class)
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface OnClick {
    
        /**
         * 控件id
         *
         * @return
         */
        @IdRes int[] value();
    }
    

    长按事件的注解

    @Event(listenerSetter = "setOnLongClickListener", listenerType = View.OnLongClickListener.class)
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface OnLongClick {
    
        /**
         * 控件id
         *
         * @return
         */
        @IdRes int[] value();
    }
    

    注解的使用

    @OnClick({R.id.btn1, R.id.btn2})
        public void click(View view) {
            int id = view.getId();
            switch (id) {
                case R.id.btn1:
                    Toast.makeText(getApplicationContext(), "按钮1点击了", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.btn2:
                    Toast.makeText(getApplicationContext(), "按钮2点击了", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    
        @OnLongClick({R.id.btn1, R.id.btn2})
        public boolean longClick(View view) {
            int id = view.getId();
            switch (id) {
                case R.id.btn1:
                    Toast.makeText(getApplicationContext(), "按钮1长按了", Toast.LENGTH_SHORT).show();
                    break;
                case R.id.btn2:
                    Toast.makeText(getApplicationContext(), "按钮2长按了", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
            return true;
        }
    

    Inject方法

     /**
         * 注入
         *
         * @param target
         */
        public static void inject(Object target) {
            if (target == null) {
                return;
            }
            injectEvent(target);
        }
    
    /**
         * 事件注入
         *
         * @param target 需要注入的组件
         */
        private static void injectEvent(Object target) {
            Class<?> clazz = target.getClass();
            Method[] methods = clazz.getDeclaredMethods();
            for (Method method : methods) {
                Annotation[] annotations = method.getAnnotations();
                for (Annotation annotation : annotations) {
                    Class<? extends Annotation> annotationType = annotation.annotationType();
                    boolean annotationPresent = annotationType.isAnnotationPresent(Event.class);
                    if (!annotationPresent) {
                        continue;
                    }
                    Event event = annotationType.getAnnotation(Event.class);
                    String listenerSetter = event.listenerSetter();
                    Class<?> listenerType = event.listenerType();
                    try {
                        Method valueMethod = annotationType.getDeclaredMethod("value");
                        valueMethod.setAccessible(true);
                        int[] viewIds = (int[]) valueMethod.invoke(annotation);
                        for (int viewId : viewIds) {
                            Method findViewByIdMethod = clazz.getMethod("findViewById", int.class);
                            View view = (View) findViewByIdMethod.invoke(target, viewId);
                            if (view == null) {
                                continue;
                            }
                            ListenerInvocationHandler listenerInvocationHandler = new ListenerInvocationHandler(target, method);
                            Object proxyInstance = Proxy.newProxyInstance(InjectUtil.class.getClassLoader(), new Class[]{listenerType}, listenerInvocationHandler);
                            Method listenerSetterMethod = view.getClass().getMethod(listenerSetter, listenerType);
                            listenerSetterMethod.setAccessible(true);
                            listenerSetterMethod.invoke(view, proxyInstance);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
    
            }
        }
    
        static class ListenerInvocationHandler implements InvocationHandler {
    
            private Object target;
    
            private Method method;
    
            public ListenerInvocationHandler(Object target, Method method) {
                this.target = target;
                this.method = method;
            }
    
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                return this.method.invoke(target, args);
            }
        }
    

    相关文章

      网友评论

          本文标题:IOC之运行时注入-实现Activity的布局注入+控件注入+事

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