美文网首页面试题安卓基础
手写一套简易的IOC框架---FindViewById

手写一套简易的IOC框架---FindViewById

作者: 加个标志位 | 来源:发表于2019-07-21 18:18 被阅读0次

    一. 反射
    1.反射的简介
    JAVA反射机制是在运行状态中,对于任意一个实体类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性并且能进行修改;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。

    2.反射的作用
    反射机制允许程序在运行时取得任何一个已知名称的class的内部信息,包括包括其modifiers(修饰符),fields(属性),methods(方法)等,并可于运行时改变fields内容或调用methods。
    (1)进行灵活的编写代码,代码可以在运行时装配,无需在组件之间进行源代码链接,降低代码的耦合度;
    (2)还有动态代理、工厂模式的实现等等;但是需要注意的是反射使用不当会造成很高的资源消耗!
    (3)获取系统隐藏类的信息,并进行修改;

    3.获取Class 的三种方式
    (1)通过对象调用 getClass() 方法来获取,通常应用在:传入一个类型的对象,但又不知道传入的具体是什么类,用这种方法,比如传过来一个 Object 类型的对象;
    (2)直接通过 类名.class 的方式得到,该方法最为安全可靠,程序性能更高 ;
    (3)通过 Class 对象的 forName() 静态方法来获取,是最常用的方式, 但是有时候会抛出 ClassNotFoundException的 异常;
    Class c3 = Class.forName("com.aj.Person");
    (4)通过ClassLoader获取:
    ClassLoader cl = context.getClassLoader();
    @SuppressWarnings("rawtypes")
    Class SystemProperties = cl.loadClass("android.os.SystemProperties");

    通过 Class 类获取成员变量、成员方法、接口、超类、构造方法等查阅 API 可以看到 Class 有很多方法:
    getName():获得类的完整名字。
    getFields():获得类的public类型的属性。
    getDeclaredFields():获得类的所有属性。包括private 声明的和继承类
    getMethods():获得类的public类型的方法。
    getDeclaredMethods():获得类的所有方法。包括private 声明的和继承类
    getMethod(String name, Class[] parameterTypes):获得类的特定方法,name参数指定方法的名字,parameterTypes 参数指定方法的参数类型。
    getConstructors():获得类的public类型的构造方法。
    getConstructor(Class[] parameterTypes):获得类的特定构造方法,parameterTypes 参数指定构造方法的参数类型。
    newInstance():通过类的不带参数的构造方法创建这个类的一个对象。

    二. 注解

    1. @Retention代表注解的保留策略:
      (1)RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;
      (2)RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,无法通过反射读取,这是默认的生命周期;
      (3)RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在,可以通过反射读取;

    这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码,生命周期长度 SOURCE < CLASS < RUNTIME ,所以前者能作用的地方后者一定也能作用。一般如果需要在运行时去动态获取注解信息,那只能用 RUNTIME 注解;如果要在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife),就用 CLASS注解;如果只是做一些检查性的操作,比如 @Override 和@SuppressWarnings,则可选用 SOURCE 注解。

    1. @Target代表注解可能出现的语法位置,即可以在哪里使用定义的注解,可选的位置如下:
      ElementType.TYPE类、接口(包括注解类型)或枚举声明
      ElementType.FIELD字段声明
      ElementType.METHOD方法声明
      ElementType.PARAMETER方法的参数声明
      ElementType.CONSTRUCTOR类的构造法声明
      ElementType.LOCAL_VARIABLE局部变量声明
      ElementType.ANNOTATION_TYPE注解声明
      ElementType.PACKAGE包声明
      ElementType.TYPE_PARAMETERJDK1.8新加的,类型参数声明
      ElementType.TYPE_USEJDK1.8新加的,类型使用声明

    三. IOC注解框架
    本次学习和笔记的内容就是通过使用上述的反射和注解,写一套FindViewById的IOC框架,以减少在项目中重复写相同的代码,并加深对反射和注解的理解;

    1. 注解:
      FindViewById的注解
    package com.bombking.bkchat.baselib.ioc;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface FindViewById {
        //表示可以传值
        int value();
    }
    
    

    点击事件的注解

    package com.bombking.bkchat.baselib.ioc;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME) //运行时
    public @interface OnClick {
        int[] value();
    }
    
    
    1. IOC工具类
    package com.bombking.bkchat.baselib.ioc;
    
    import android.app.Activity;
    import android.view.View;
    
    import com.bombking.bkchat.baselib.net.CheckNet;
    import com.bombking.bkchat.baselib.net.NetManagerUtil;
    import com.bombking.bkchat.utils.ToastUtils;
    
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    
    public class ViewIoc {
    
        // 绑定注解
        public static void bindInit(Activity activity){
            findViewById(activity);
    
            onClickListener(activity);
        }
    
        /**
         * find view by id
         * @param activity
         */
        private static void findViewById(Activity activity){
            Class<?> clazz = activity.getClass();
    
            //1.获取activity中的所有属性;  clazz.getFields() -> 获取public的属性
            Field[] fields = clazz.getDeclaredFields();
    
            //2.遍历获取注解的属性;
            for (Field field : fields){
                FindViewById findViewById =  field.getAnnotation(FindViewById.class);
    
                //3.遍历所有的属性,如果注解不是空,获取注解的viewId
                if (findViewById != null){
                    int viewId = findViewById.value();
                    //4.findViewById,获取view;
                    View view = activity.findViewById(viewId);
    
                    //5.把view动态的注入activity属性;相当于把注解获取到的属性赋值给activity中的成员变量;
                    try {
                        field.setAccessible(true);//确定可以操作私有的属性
                        field.set(activity, view);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 点击事件
         * @param activity
         */
        private static void onClickListener(final Activity activity) {
            Class<?> clazz = activity.getClass();
            Method[] methods = clazz.getDeclaredMethods();
            for (final Method method : methods){
                 OnClick onClick = method.getAnnotation(OnClick.class);
                 final boolean isNet = method.getAnnotation(CheckNet.class) != null;//加入一个网络检查注解
                 if (onClick != null){
                     //获取所有方法上面注解的View的id值;
                     int[] values = onClick.value();
                     //遍历找出所有的view;
                     for (int viewId : values){
                         View view = activity.findViewById(viewId);
                         view.setOnClickListener(new View.OnClickListener() {
                             @Override
                             public void onClick(View v) {
                                 //检查网络是否可用
                                 if (isNet){
                                     if (!NetManagerUtil.cureentNetIsAvailable(activity)){
                                         ToastUtils.getInstance().toastShort("当前无网络");//无网络时的操作
                                         return;
                                     }
                                 }
                                 //使用反射
                                 method.setAccessible(true);//私有方法权限
                                 try {
                                     method.invoke(activity);
                                 } catch (Exception e) {
                                     try {
                                         method.invoke(activity, view);//兼容有view的情况
                                     } catch (Exception e1) {
                                         e1.printStackTrace();
                                     }
                                     e.printStackTrace();
                                 }
                             }
                         });
                     }
                 }
            }
        }
    
    }
    
    1. 使用
     @FindViewById(R.id.btn_register)
        Button button;
    
        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ViewIoc.bindInit(this);
        }
    
        @OnClick({R.id.bt1,R.id.bt2})
        @CheckNet
        private void getData(){
            Toast.makeText(this,"请求后台数据",Toast.LENGTH_LONG).show();
        }
    
    

    结束语:我是一个入行不久的小菜鸟,如有不正确的地方,欢迎指正,感谢你的阅读;

    相关文章

      网友评论

        本文标题:手写一套简易的IOC框架---FindViewById

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