美文网首页
Android自定义注解与反射

Android自定义注解与反射

作者: lp209 | 来源:发表于2018-01-05 22:51 被阅读0次

    反射

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

    1.获取Class对象

    举个例子,我们要获取MainActivity的Class对象,有以下几种方式

    1.Class<MainActivity> clazz = MainActivity.class;
    2.Class<? extends MainActivity> clazz = this.getClass();
    3.Class<?> clazz = Class.forName("utils.utilcode.blankj.com.dongtai.MainActivity");

    2.通过Class对象获取构造方法,成员变量,方法等

    构造函数

    getConstructor();//获取本类或父类中public修饰的无参构造函数
    getDeclaredConstructor();//获取本类无参构造函数
    getDeclaredConstructor(Class<?>... parameterTypes): 获取本类中指定参数的构造器

    成员变量

    getFields(): 获取本类或父类中所有public属性
    getField(String name): 获取本类或父类中特定名字的public属性
    getDeclaredFields(): 获取本类中声明的所有属性

    获取方法

    getMethods(): 获取本类或父类中所有public方法(包括构造器方法)
    getMethod(String name, Class<?>... parameterTypes): 获取本类或父类中特定名字和参数的public方法
    getDeclaredMethods(): 获取本类中声明的所有方法(包括非public但不包括继承来的)
    getDeclaredMethod(String name, Class<?>... parameterTypes): 获取本类中声明的特定名字和参数的方法(最常用)

    获取注解

    getAnnotation(Class<A> annotationClass): 获取这个元素上指定类型的注解(常用)
    getDeclaredAnnotations(): 获取直接标注在这个元素上的注解

    父类子类(接口)相关

    getSuperclass(): 返回本类的父类
    getGenericSuperclass(): 以Type的形式返回本类的父类, 带有范型信息
    getInterfaces(): 返回本类直接实现的接口
    getGenericInterfaces(): 以Type的形式返回本类直接实现的接口, 带有范型信息
    

    自定义注解

    新建一个InjectView类
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface InjectView {
        int value();
    }
    

    Target表示作用范围,这里表示作用于成员变量

    /** Targe表示作用范围
     * TYPE 作用对象类/接口/枚举
     * FIELD 成员变量
     * METHOD 成员方法
     * PARAMETER 方法参数
     * ANNOTATION_TYPE 注解的注解
     */
    

    Retention指定了注解有效期直到运行时时期
    value就是用来指定id,也就是findViewById的参数

    在MainActivity中添加注解
        @InjectView(R.id.bind_view_btn)
        Button mBindView;
        @InjectView(R.id.textView)
        TextView textView;
    
    在MainActivity的OnCreate方法中进行注入
      super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            Utils.injectView(this);
    
    新建Utils这个类,创建injectView方法

    代码中的每一行都有详细的注释

        public static void injectView(Activity activity) {
            if (null == activity) return;
            //获取activity的.class对象
            Class<? extends Activity> activityClass = activity.getClass();
    //        获取所有成员变量集合
            Field[] declaredFields = activityClass.getDeclaredFields();
            for (Field field : declaredFields) {
                //找到有@InjectView注解的成员变量
                if (field.isAnnotationPresent(InjectView.class)) {
                    //得到注解类的对象
                    InjectView annotation = field.getAnnotation(InjectView.class);
                    //找到VIew的id
                    int value = annotation.value();
                    try {
                        //找到findViewById方法
                        Method findViewByIdMethod = activityClass.getMethod("findViewById", int.class);
                       //暴力访问,可获取私有方法
                        findViewByIdMethod.setAccessible(true);
                        View view = (View) findViewByIdMethod.invoke(activity, value);
                        //将结果赋值给成员变量
                        field.set(activity, view);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    

    首先是获取activity的.class对象,通过Class对象找到该类中所有的成员变量。继而找到有InjectView注解的成员变量,然后得到注解类的对象,找到VIew的id,通过反射获取Activity的findViewById方法,然后将结果传给成员变量。

    注解onClick点击事件

    新建一个onClick类
    
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    @EventType(listenerType = View.OnClickListener.class, listenerSetter = "setOnClickListener", methodName = "onClick")
    public @interface onClick {
        int[] value();
    }
    
       Target指定了onClick注解作用对象是成员方法
       Retention指定了onClick注解有效期直到运行时时期
       value就是用来指定id,也就是findViewById的参数
    
    新建一个EventType类
    @Target(ElementType.ANNOTATION_TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface EventType {
        Class listenerType();
        String listenerSetter();
        String methodName();
    }
    
    Target指定了EventType注解作用对象是注解,也就是注解的注解
    Retention指定了EventType注解有效期直到运行时时期
    listenerType用来指定点击监听类型,比如OnClickListener
    listenerSetter用来指定设置点击事件方法,比如setOnClickListener
    methodName用来指定点击事件发生后会回调的方法,比如onClick
    

    MainActivity中添加注解

        @onClick({R.id.button})
        public void onclick(View view) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            switch (view.getId()) {
                case R.id.button:
                    Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    

    记得注入onClick事件

      setContentView(R.layout.activity_main);
      Utils.injectView(this);
      Utils.injectEvent(this);
    

    创建injectEvent方法

        public static void injectEvent(Activity activity) {
            if (null == activity) {
                return;
            }
            //获取activity的.class对象
            Class<? extends Activity> activityClass = activity.getClass();
            // 获取所有方法
            Method[] declaredMethods = activityClass.getDeclaredMethods();
            for (Method method : declaredMethods) {
           //找到有@onClick注解的方法
                if (method.isAnnotationPresent(onClick.class)) {
                    //得到注解类的对象
                    onClick annotation = method.getAnnotation(onClick.class);
                    //获取控件id的集合
                    int[] value = annotation.value();
                    //获取注解类中的注解
                    EventType eventType = annotation.annotationType().getAnnotation(EventType.class);
                   //得到注解类中的注解的属性
                    Class listenerType = eventType.listenerType();
                    String listenerSetter = eventType.listenerSetter();
                    String methodName = eventType.methodName();
                    //创建InvocationHandler和动态代理(代理要实现listenerType,这个例子就是处理onClick点击事件)
                    ProxyHandler proxyHandler = new ProxyHandler(activity);
                    //得到实现类的接口对象(1:实现类的类加载器2:实现类的接口数组3:proxyHandler对象(当执行接口的方法时会先执行里面的invoke,再执行实际方法,实现动态代理)
                    Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, proxyHandler);
                    // 将Activity中被注解的方法传至ProxyHandler 中
                    proxyHandler.mapMethod(methodName, method);
                    try {
                        for (int id : value) {
                            //找到Button
                            Method findViewByIdMethod = activityClass.getMethod("findViewById", int.class);
                            findViewByIdMethod.setAccessible(true);
                            View btn = (View) findViewByIdMethod.invoke(activity, id);
                            //根据listenerSetter方法名和listenerType方法参数找到method
                            Method listenerSetMethod = btn.getClass().getMethod(listenerSetter, listenerType);
                            listenerSetMethod.setAccessible(true);
                            //反射执行setOnclickListener
                            listenerSetMethod.invoke(btn, listener);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    

    ProxyHandler代码如下

    public class ProxyHandler implements InvocationHandler {
    
        private WeakReference<Activity> mHandlerRef;
    
        private HashMap<String, Method> mMethodHashMap;
    
        public ProxyHandler(Activity activity) {
            mHandlerRef = new WeakReference<>(activity);
            mMethodHashMap = new HashMap<>();
        }
    
        public void mapMethod(String name, Method method) {
            mMethodHashMap.put(name, method);
        }
    
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            Log.i("TAG", "method name = " + method.getName() + " and args = " + Arrays.toString(args));
    
            Object handler = mHandlerRef.get();
    
            if (null == handler) return null;
    
            String name = method.getName();
    
            //得到injectEvent中传递过来的被@onClick注解的方法
            Method realMethod = mMethodHashMap.get(name);
            if (null != realMethod){
           //反射执行Activity中onClick方法
                return realMethod.invoke(handler, args);
            }
    
            return null;
        }
    }
    

    重点是动态代理。这里简单分析一下ProxyHandler 类,它主要是用于动态代理。这里为什么要用动态代理呢!是因为当我们点击按钮时,会触发onClick方法,而我们需要在onClick里面执行我们注解的方法,所以必须对Activity动态代理。

    Object listener = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{listenerType}, proxyHandler);
    

    第一个参数为接口的加载器(本例中为Activity的接口View.OnclickListener),第二个参数为Activity的接口数组(View.OnclickListener),第三个参数为ProxyHandler 对象。返回一个实现类(View.OnclickListener)的对象。由于在injectEvent中我们反射执行了setOnClickListener,当用户触发点击事件时,会执行
    接口中的方法(setOnClickListener的onClick方法)然后会执行ProxyHandler 类中的invoke方法,然后反射得到Activity中被@onClick注解的方法即onClick方法,然后反射调用它,执行注解中的方法。因此

        @onClick({R.id.button})
        public void onclick(View view) {
            AlertDialog.Builder builder = new AlertDialog.Builder(this);
            switch (view.getId()) {
                case R.id.button:
                    Toast.makeText(this, "click", Toast.LENGTH_SHORT).show();
                    break;
            }
        }
    

    不管方法名是什么,当用户点击后,都会反射执行对应方法体

    注解类对象

    这次我们通过注解得到Animal 的对象

    
    public class Animal implements Fly, Run {
    
        public static final String TAG = "ProxyTest";
    
        public Animal() {
        }
    
        @Override
        public void fly() {
            System.out.println("Animal fly");
        }
    
        @Override
        public void run() {
            System.out.println("Animal run");
    
        }
    }
    

    同理新建一个自定义注解

    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Inject {
    }
    

    在MainActivity中进行注解

       @Inject
        Animal mAnimal;
    

    然后注入

      setContentView(R.layout.activity_main);
      Utils.injectView(this);
      Utils.injectEvent(this);
      InjectUtils.inject(this);
    

    InjectUtil

    public class InjectUtils {
        public static void inject(Activity activity) {
            if (null == activity) {
                return;
            }
            //获取activity的.class对象
            Class<? extends Activity> activityClass = activity.getClass();
            //        获取所有成员变量集合
            Field[] declaredFields = activityClass.getDeclaredFields();
            for (Field field : declaredFields) {
                //找到有Inject注解的成员变量
                if (field.isAnnotationPresent(Inject.class)) {
                    //得到成员变量的类型的字节码文件
                    Class<?> clazz = field.getType();
                    Constructor con = null;
                   try {
                        //得到无参构造
                       con = clazz.getConstructor();
                        //获取Animal其对象
                       Object instance = con.newInstance();
                        //赋值给field
                      field.set(activity,instance);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    

    本文中所有Demo的GitHub地址为https://github.com/zhonghongwen/Custom-Annotation

    相关文章

      网友评论

          本文标题:Android自定义注解与反射

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