美文网首页
写给自己的IoC

写给自己的IoC

作者: 无良安生 | 来源:发表于2021-04-13 17:33 被阅读0次

    IoC是什么呢?
    我自己也整不明白,我怎么解释,自己百度去吧

    今天干啥,那就弄个注解加载布局加载view 设置监听吧
    废话不多说,反正我感觉你也不想听,那就上代码吧

    首先看界面

    @ContentView(layoutRes = R.layout.activity_main)
    public class MainActivity extends AppCompatActivity {
    
        @InjectViews(viewRes = R.id.tv_test)
        TextView textView;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            InitLayout.inject(this);
            textView.setText("来呀打我呀!");
        }
    
        @OnClick(viewIds = {R.id.tv_demo})
        public void click(View view){
            Toast.makeText(getBaseContext(),"click",Toast.LENGTH_LONG).show();
        }
    
        @OnLongClick(viewIds = {R.id.tv_test})
        public boolean longClic(View view) {
            Toast.makeText(getBaseContext(),"longClic",Toast.LENGTH_LONG).show();
            return false;
        }
    }
    

    布局不给了,就是两个文本,自己写

    然后再给你看看注解

    注解activity 布局

    /*
              TYPE: 用于描述类、接口(包括注解类型) 或enum声明 Class, interface (including annotation type), or enum declaration
              FIELD: 用于描述域 Field declaration (includes enum constants)
              METHOD: 用于描述方法 Method declaration
              PARAMETER: 用于描述参数 Formal parameter declaration
              CONSTRUCTOR: 用于描述构造器 Constructor declaration
              LOCAL_VARIABLE: 用于描述局部变量 Local variable declaration
              ANNOTATION_TYPE: Annotation type declaration
              PACKAGE: 用于描述包 Package declaration
              TYPE_PARAMETER: 用来标注类型参数 Type parameter declaration
              TYPE_USE: 能标注任何类型名称 Use of a type
    
              RetentionPolicy
              SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
              CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;
              RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
    
     */
    
    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ContentView {
        int layoutRes();
    }
    

    注解初始化view

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface InjectViews {
        int viewRes() default 0;
    }
    

    注解设置事件

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @ClickBase(listener = "setOnClickListener" ,callback = "onClick",listenerType = View.OnClickListener.class)
    public @interface OnClick {
        int[] viewIds();
    }
    

    再看这个ClickBase

    @Target(ElementType.ANNOTATION_TYPE)//在注解之上
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ClickBase {
    
        //设置事件得方法名称
        String listener();
        //事件回调的类行
        Class<?> listenerType();
        //回调的方法名称
        String callback();
    }
    
    

    好了前期准备的差不多了,到核心技术了,不要眨眼

    public class InitLayout {
    
        public static void inject(Activity activity) {
            injectLayout(activity);
            injectViews(activity);
            injectEvents(activity);
        }
    
        private static void injectViews(Activity activity) {
            Class<? extends Activity> clazz = activity.getClass();
            //获取到该方法下的变量(全部,不论访问权限)
            Field[] fields = clazz.getDeclaredFields();
            for (Field field : fields) {
                //获取到注解 InjectViews
                InjectViews injectViews = field.getAnnotation(InjectViews.class);
                if (injectViews != null) {
                    //获取到注解中的布局ID
                    int viewid = injectViews.viewRes();
    
    //               View view = activity.findViewById(viewid)  //这个是方式之一,但是不够档次
                    try {
                        //获取指定类 (getMethod 第一个是方法名 第二个是参数的集合 可以用Object[])
                        Method method = clazz.getMethod("findViewById", int.class);
                        //执行getMethod获取到的方法
                        Object view = method.invoke(activity, viewid);
                        //给属性field赋值
                        field.set(activity, view);
                        //设置访问权限
                        field.setAccessible(true);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
    
                }
            }
        }
    
        /**
         * 设置点击事件
         *
         * @param activity
         */
        private static void injectEvents(Activity activity) {
            Class<? extends Activity> clazz = activity.getClass();
            //获取到所有public的方法,包括父类
            Method[] methods = clazz.getMethods();
    
            //遍历方法,拿到有自己注解的方法
            for (Method method : methods) {
                //一个方法有多个注解
                Annotation[] annotations = method.getAnnotations();
                /*
                    遍历注解,拿到自己定义的注解
                    其实也可以这样拿到 某个注解 -》ContentView contentView = clazz.getAnnotation(ContentView.class);
                    但是这样会写死,只支持方法上一种注解
                    遍历所有注解,然后拿到每个注解上带有ClickBase注解的 注解 真是妙哉妙哉妙哉
                    后面只要写各种注解上使用ClickBase注解就可以
                 */
                for (Annotation annotation : annotations) {
                    //获取到注解上面的注解
                    Class<? extends Annotation> annotationType = annotation.annotationType();
                    if (annotationType != null) {
                        try {
                            //拿到方法注解上的注解
                            ClickBase clickBase = annotationType.getAnnotation(ClickBase.class);
    
                            if (clickBase != null) {
                                //拿到ClickBase的三个重要的值
                                String callbackStr = clickBase.callback();
                                String listenerStr = clickBase.listener();
                                Class<?> listenerType = clickBase.listenerType();
                                //Aop
                                ClickListenerHandler handler = new ClickListenerHandler(activity);
                                //把方法都添加到ClickListenerHandler中
                                handler.addMethod(callbackStr, method);
                                //实现代理
                                Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, handler);
    
                                /*
                                      获取ids的也是可以这样写
                                      OnClick onClick = (OnClick) annotation;
                                      int[] ids = onClick.viewIds();
                                      但是这样又写死了
                                 */
    
    //                            //获取控件id 的方法
                                Method valueMethod = annotationType.getDeclaredMethod("viewIds");
    //                            //执行获取id 的方法,拿到id列表
                                int[] ids = (int[]) valueMethod.invoke(annotation);
    
    
                                //遍历控件id列表
                                for (int id : ids) {
                                    View view = activity.findViewById(id);
                                    //获取到设置监听的方法
                                    Method clickListener = view.getClass().getMethod(listenerStr, listenerType);
    
                                    clickListener.invoke(view, listener);
                                }
                            }
    
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }
    
            }
    
    
        }
    
        private static void injectLayout(Activity activity) {
            //拿到activity的反射
            Class<? extends Activity> clazz = activity.getClass();
            //拿到反射类的注解
            ContentView contentView = clazz.getAnnotation(ContentView.class);
            if (contentView != null) {
                //拿到注解的值
                int layout = contentView.layoutRes();
                try {
                    //获取指定类 (getMethod 第一个是方法名 第二个是参数的集合 可以用Object[])
                    Method method = clazz.getMethod("setContentView", int.class);
                    //执行getMethod获取到的方法
                    method.invoke(activity, layout);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    

    好了,这个就是核心代码了

    发现了没,我们要设置事件的时候,就要引入一个东西 Aop
    用注解的方法,去替掉回调的方法,nice 看懂了么,不懂没关系,我是不会多说的,耗子尾汁

    先看看InvocationHandler实现类

    public class ClickListenerHandler implements InvocationHandler {
    
        //执行方法的对象
        private Object target;
    
        //替换掉回调的方法
        private HashMap<String, Method> methodHashMap = new HashMap();
    
        public ClickListenerHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            String name = method.getName();
            method = methodHashMap.get(name);
            if (method != null) {
                return method.invoke(target, args);
            }
            return null;
        }
    
        public void addMethod(String methodName,Method method){
            methodHashMap.put(methodName,method);
        }
    }
    

    行了,代码上了,然后扩展吧

    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @ClickBase(listener = "setOnLongClickListener" ,callback = "onLongClick",listenerType = View.OnLongClickListener.class)
    public @interface OnLongClick {
    
        int[] viewIds();
    }
    

    剩下的自己去鸟补吧

    相关文章

      网友评论

          本文标题:写给自己的IoC

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