Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类

Stella981
• 阅读 821

1. Android中的IOC(DI)框架

1.1 ViewUtils简介(xUtils中的四大部分之一)

  • IOC: Inverse of Controller 控制反转。

  • DI: Dependency Inject 依赖注入

  • 完全注解方式就可以进行UI绑定和事件绑定。

  • 无需findViewById和setClickListener等。

1.2 ViewUtils使用

compile 'org.xutils:xutils:3.5.0'
compile 'com.jiechic.library:xUtils:2.6.14'

// xUtils的view注解要求必须提供id,以使代码混淆不受影响。
@ViewInject(R.id.textView)
TextView textView;

//@ViewInject(vale=R.id.textView, parentId=R.id.parentView)
//TextView textView;

@ResInject(id = R.string.label, type = ResType.String)
private String label;

// 取消了之前使用方法名绑定事件的方式,使用id绑定不受混淆影响
// 支持绑定多个id @OnClick({R.id.id1, R.id.id2, R.id.id3})
// or @OnClick(value={R.id.id1, R.id.id2, R.id.id3}, parentId={R.id.pid1, R.id.pid2, R.id.pid3})
// 更多事件支持参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。
@OnClick(R.id.test_button)
public void testButtonClick(View v) { // 方法签名必须和接口中的要求一致
    ...
}
...
//在Activity中注入:
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    ViewUtils.inject(this); //注入view和事件
    ...
    textView.setText("some text...");
    ...
}
//在Fragment中注入:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View view = inflater.inflate(R.layout.bitmap_fragment, container, false); // 加载fragment布局
    ViewUtils.inject(this, view); //注入view和事件
    ...
}
//在PreferenceFragment中注入:
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    ViewUtils.inject(this, getPreferenceScreen()); //注入view和事件
    ...
}
// 其他重载
// inject(View view);
// inject(Activity activity)
// inject(PreferenceActivity preferenceActivity)
// inject(Object handler, View view)
// inject(Object handler, Activity activity)
// inject(Object handler, PreferenceGroup preferenceGroup)
// inject(Object handler, PreferenceActivity preferenceActivity)

1.3 反射:调用(设置)类私有成员和私有方法

定义的User类:

package cn.Douzi.Annotation_Test;

public class User {
    
    @ViewInject(age=23, name="张三")
    private String name;
    
    private int age;
    
    private String eat(String eat) {
        System.out.println("eat: " + eat);
        return eat + "真好吃";
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "User [name =" + name + ", age = " + age + "]";
    }
}

反射调用私有对象,设置该类对象:

Class cls = User.class;
        
        /**
         * 2. 将这个字节码中的name字段获取到
         */
//        cls.getField(name);           //这个方法只能获取声明为public的字段
        Field declaredField = cls.getDeclaredField("name");
        Field declaredFieldAge = cls.getDeclaredField("age");      declaredField.setAccessible(true);  //设置允许访问,其实就是允许暴力反射
        declaredFieldAge.setAccessible(true);
            
            //给user对象的declaredField设置为name
        declaredField.set(user, name);
        declaredFieldAge.set(user, age);

反射调用私有函数,设置该类函数:

        //通过反射调用User对象的eat方法
        Method declaredMethod = cls.getDeclaredMethod("eat", String.class);
        //暴力反射调用改方法
        declaredMethod.setAccessible(true);
        Object result = declaredMethod.invoke(user, "牛肉拉面");   # 设置函数参数列表
        
        System.out.println(result);

1.4 反射 和 自定义注解类

自定义注解类:

package cn.Douzi.Annotation_Test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.lang.model.element.Element;

/**
 * @Taget(ElementType.METHOD)   //用于方法
 *
 */
@Target(ElementType.FIELD)         //用于限制当前自定义注解类作用的对象
//@Retention(RetentionPolicy.SOURCE) //该注解只会作用在源码阶段,当源码编译成字节码时注解信息就被清除
//@Retention(RetentionPolicy.CLASS)  //该注解只会作用在字节码阶段,但是当虚拟机加载这个字节码时,注解信息被清除
@Retention(RetentionPolicy.RUNTIME) //该注解类一直保留到加载到虚拟机中
public @interface ViewInject {
    
    int age();
    String name();
        
}

package cn.Douzi.Annotation_Test;

public class User {
    
    @ViewInject(age=23, name="张三")
    private String name;
    
    private int age;
    
    private String eat(String eat) {
        System.out.println("eat: " + eat);
        return eat + "真好吃";
    }

    @Override
    public String toString() {
        // TODO Auto-generated method stub
        return "User [name =" + name + ", age = " + age + "]";
    } 
}

1.5 测试自定义注解类

如何获取自定义注解对象(User)中参数

Class cls = User.class;
        
        /**
         * 2. 将这个字节码中的name字段获取到
         */
//        cls.getField(name);           //这个方法只能获取声明为public的字段
        Field declaredField = cls.getDeclaredField("name");
        Field declaredFieldAge = cls.getDeclaredField("age");
        
        /**
         * 3. 将当前字段上的注解对象获取到
         */
        ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
        if (viewInject != null) {
            /*
             * 4.获取自定义注解对象中的参数
             */
            int age = viewInject.age();
            String name = viewInject.name();
            System.out.println("name: " + name + ", age = " + age);
        }

测试是否能获取到注解对象(完整代码)

package cn.Douzi.Annotation_Test;

import java.lang.reflect.Field;

public class AnnotationMainTest {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException {

        //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性
        //将name设置给User对象的name属性
        User user = new User();
        /**
         * 反射:
         * 1. 先去获取到User的字节码
         */
//        user.getClass();
//        User.class
//        Class.forName("xxxx")
        Class cls = User.class;
        
        /**
         * 2. 将这个字节码中的name字段获取到
         */
//        cls.getField(name);           //这个方法只能获取声明为public的字段
        Field declaredField = cls.getDeclaredField("name");
        
        /**
         * 3. 将当前字段上的注解对象获取到
         */
        ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
        if (viewInject != null) {
            /*
             * 4.获取自定义注解对象中的参数
             */
            int age = viewInject.age();
            String name = viewInject.name();
            System.out.println("name: " + name + ", age = " + age);
        }
        else {
            System.out.println("字段上无自定义注解");
        }
        
    }

}

1.6 通过暴力反射将获取到的注解值设置给User对象 (完整代码)

package cn.Douzi.Annotation_Test;

import java.lang.reflect.Field;

public class AnnotationMainTest {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {

        //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性
        //将name设置给User对象的name属性
        User user = new User();
        /**
         * 反射:
         * 1. 先去获取到User的字节码
         */
//        user.getClass();
//        User.class
//        Class.forName("xxxx")
        Class cls = User.class;
        
        /**
         * 2. 将这个字节码中的name字段获取到
         */
//        cls.getField(name);           //这个方法只能获取声明为public的字段
        Field declaredField = cls.getDeclaredField("name");
        Field declaredFieldAge = cls.getDeclaredField("age");
        
        /**
         * 3. 将当前字段上的注解对象获取到
         */
        ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
        if (viewInject != null) {
            /*
             * 4.获取自定义注解对象中的参数
             */
            int age = viewInject.age();
            String name = viewInject.name();
            System.out.println("name: " + name + ", age = " + age);
            
            /*
             * 5. 通过反射将这个两个值设置给User对象
             */
            declaredField.setAccessible(true);  //设置允许访问,其实就是允许暴力反射
            declaredFieldAge.setAccessible(true);
            
            //给user对象的declaredField设置为name
            declaredField.set(user, name);
            declaredFieldAge.set(user, age);
            
            System.out.println(user.toString());
            
            
        }
        else {
            System.out.println("字段上无自定义注解");
        }
        
    }

}

Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类

1.7 通过反射调用User对象的eat方法 (完整代码)

package cn.Douzi.Annotation_Test;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class AnnotationMainTest {

    public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {

        //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性
        //将name设置给User对象的name属性
        User user = new User();
        /**
         * 反射:
         * 1. 先去获取到User的字节码
         */
//        user.getClass();
//        User.class
//        Class.forName("xxxx")
        Class cls = User.class;
        
        /**
         * 2. 将这个字节码中的name字段获取到
         */
//        cls.getField(name);           //这个方法只能获取声明为public的字段
        Field declaredField = cls.getDeclaredField("name");
        Field declaredFieldAge = cls.getDeclaredField("age");
        
        /**
         * 3. 将当前字段上的注解对象获取到
         */
        ViewInject viewInject = declaredField.getAnnotation(ViewInject.class);
        if (viewInject != null) {
            /*
             * 4.获取自定义注解对象中的参数
             */
            int age = viewInject.age();
            String name = viewInject.name();
            System.out.println("name: " + name + ", age = " + age);
        
            /*
             * 5. 通过反射将这个两个值设置给User对象
             */
            declaredField.setAccessible(true);  //设置允许访问,其实就是允许暴力反射
            declaredFieldAge.setAccessible(true);
            
            //给user对象的declaredField设置为name
            declaredField.set(user, name);
            declaredFieldAge.set(user, age);
            
            System.out.println(user.toString());
        }
        else {
            System.out.println("字段上无自定义注解");
        }
        
        //通过反射调用User对象的eat方法
        Method declaredMethod = cls.getDeclaredMethod("eat", String.class);
        //暴力反射调用改方法
        declaredMethod.setAccessible(true);
        Object result = declaredMethod.invoke(user, "牛肉拉面");
        
        System.out.println(result);
    }

}

Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类

2. 自定义ViewUtils (Android)

布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="com.douzi.myviewutils.MainActivity">

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!!!!"
        android:textSize="20dp"
        />

    <TextView
        android:id="@+id/tv2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World2222222222!!!!"
        android:textSize="20dp"
        />


    <Button
        android:id="@+id/btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="OKKKK"
        />

</LinearLayout>

注解类:

package com.douzi.myviewutils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by Douzi on 2019/3/11.
 */

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

    int value();      //这里必须叫value,否则绑定的时候需要写: xx=R.id.xx

}

实现 ViewUtils的inject方法(使用反射)

package com.douzi.myviewutils;

import android.app.Activity;
import android.view.View;

import java.lang.reflect.Field;

/**
 * Created by Douzi on 2019/3/11.
 */

public class ViewUtils {

    public static void inject(Activity activity) {

        //绑定控件
        try {
            bindView(activity);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

    private static void bindView(Activity activity) throws IllegalAccessException {
        /**
         *  1. 获取Activity的字节码
         */
        Class clazz = activity.getClass();
        /**
         *  2. 获取到该字节码中所有的Filed
         */
        Field[] declaredFields = clazz.getDeclaredFields();

        /**
         *  3. 判断哪些是我们想要的字段(只有添加了ViewInject注解的字段)
          */
        for (Field field : declaredFields) {
            //获取字段上面的注解
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            if (viewInject != null) {
                /**
                 *  4. 获取当前注解的值
                 */
                int resId = viewInject.value();
                /**
                 *  5. 通过调用Activity的findViewById方法,获取当前id为resId的控件
                 */
                View view = activity.findViewById(resId);
                /**
                 *  6. 将当前的View设置给当前的Filed
                 */
                field.setAccessible(true);
                //给Activity对象的filed字段设置值为view对象
                field.set(activity, view);

            }
            else {
                // nothing
            }
        }
    }

}

测试注解的功能

package com.douzi.myviewutils;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @ViewInject(R.id.tv1)
    TextView textView;

    @ViewInject(R.id.tv2)
    TextView textView2;

    private int count;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewUtils.inject(this);

        textView.setText("成功了!!");
        textView2.setText("成功了2!!");

    }
}

Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类

绑定点击事件实现(实现ViewUtils的OnClick实现)

注解类

package com.douzi.myviewutils;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * Created by Douzi on 2019/3/11.
 */

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {

    int value();

}

ViewUtils里添加函数

public static void bindOnClick(final Activity activity) {
        /**
         *  1. 获取字节码
         */
        Class clazz = activity.getClass();
        /**
         *  2. 获取字节码中所有的方法
         */
        Method[] methods = clazz.getDeclaredMethods();

        /**
         *  3. 遍历方法, 找出方法上声明了OnClick注解的方法
         */
        for (final Method method : methods) {

            /**
             *  4. 获取当前方法的上的注解
             */
            OnClick onClick = method.getAnnotation(OnClick.class);

            if (onClick != null) {
                /**
                 *  5. 获取注解中的值
                 */
                int resId = onClick.value();
                /**
                 *  6. 获取到id为resId的View
                 */
                final View view = activity.findViewById(resId);
                /**
                 *  7. 将当前的View绑定点击事件
                 */
                view.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        /**
                         *  8. 通过反射调用当前的用户的方法
                         */
                        //设置可以
                        method.setAccessible(true);

                        try {
                            method.invoke(activity, view);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }

    }

public static void inject(Activity activity) {

        //绑定控件
        try {
            //绑定界面
            bindView(activity);

            //绑定方法
            bindOnClick(activity);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }

测试注解功能

package com.douzi.myviewutils;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    @ViewInject(R.id.tv1)
    TextView textView;

    @ViewInject(R.id.tv2)
    TextView textView2;

    private int count;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewUtils.inject(this);

        textView.setText("成功了!!");
        textView2.setText("成功了2!!");

    }

    @OnClick(R.id.btn)
    private void clickMe(View view) {
        Toast.makeText(this, "点击我了", Toast.LENGTH_LONG).show();
    }

}

Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类

点赞
收藏
评论区
推荐文章
blmius blmius
3年前
MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1
文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s
皕杰报表之UUID
​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为
待兔 待兔
3个月前
手写Java HashMap源码
HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22
Jacquelyn38 Jacquelyn38
3年前
2020年前端实用代码段,为你的工作保驾护航
有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )
Stella981 Stella981
3年前
KVM调整cpu和内存
一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid
Wesley13 Wesley13
3年前
mysql设置时区
mysql设置时区mysql\_query("SETtime\_zone'8:00'")ordie('时区设置失败,请联系管理员!');中国在东8区所以加8方法二:selectcount(user\_id)asdevice,CONVERT\_TZ(FROM\_UNIXTIME(reg\_time),'08:00','0
Wesley13 Wesley13
3年前
00:Java简单了解
浅谈Java之概述Java是SUN(StanfordUniversityNetwork),斯坦福大学网络公司)1995年推出的一门高级编程语言。Java是一种面向Internet的编程语言。随着Java技术在web方面的不断成熟,已经成为Web应用程序的首选开发语言。Java是简单易学,完全面向对象,安全可靠,与平台无关的编程语言。
Stella981 Stella981
3年前
Django中Admin中的一些参数配置
设置在列表中显示的字段,id为django模型默认的主键list_display('id','name','sex','profession','email','qq','phone','status','create_time')设置在列表可编辑字段list_editable
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这