android 自定义注解

在开发中注解是随处可见的,比如我们常见的@Override,@Deprecated和@SuppressWarnings。注解有很多的好处,我们也可以自定义注解。

一,元注解

我们先来看一下元注解
,在java.lang.annotation中一共提供了4个元注解@Retention、 @Target、@Inherited、@Documented。
@Target 表明我们注解可以出现的地方。是一个ElementType枚举
@Retention 这个注解的的存活时间
@Document 表明注解可以被javadoc此类的工具文档化
@Inherited 是否允许子类继承该注解,默认为false

@Rentention Rentention

@Rentention Rentention用来标记自定义注解的有效范围,他的取值有下面三种:
RetentionPolicy.SOURCE: 只在源代码中保留,主要用来增加代码的可读性或者帮助代码检查之类的,比如常见的Override;
RetentionPolicy.CLASS: 默认的选择,能把注解保留到编译后的字节码class文件中,仅仅到字节码文件中,运行时是无法得到的;
RetentionPolicy.RUNTIME: 运行时也能生效,可以通过反射获取到,一般自定义注解的时候这个用的比较多

@Target

@Target表示注解的作用范围
ElementType.TYPE:类、接口、枚举、注解类型。
ElementType.FIELD:成员变量
ElementType.METHOD:方法
ElementType.PARAMETER:参数
ElementType.CONSTRUCTOR:构造器
ElementType.LOCAL_VARIABLE:局部变量
ElementType.ANNOTATION_TYPE:注解
ElementType.PACKAGE:包

@Inherited

@Inherited表明我们标记的注解是被继承的。比如,如果一个父类使用了@Inherited修饰的注解,则允许子类继承该父类的注解。

@Document

@Document表明我们标记的注解可以被javadoc此类的工具文档化。

二,自定义注解

在android开发中我们经常会使用findViewById,下面我们来通过注解的方式替换掉findViewById方法,我们来先自定义一个注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface ViewInject {
    int value();
}

然后在写个方法

    public static void injectField(Activity activity) {
        Field[] fields = activity.getClass().getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            // 判断属性上面是否存在ViewInject注解
            if (field.isAnnotationPresent(ViewInject.class)) {
                ViewInject vi = field.getAnnotation(ViewInject.class);
                int resourceId = vi.value();// 获取控件资源id
                try {
                    field.set(activity, activity.findViewById(resourceId));// 绑定控件
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

使用的时候只需要在字段上加上注解即可

    @ViewInject(R.id.username)
    private EditText username;

然后在activity的onCreate方法中加上这样一行代码

ViewUtil.injectField(this);

这个就和EventBus注解类似。当然我们还可以在定义一个点击的方法

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int[] value();
}

上面四注解的类,下面是获取注解的方法

    public static void injectMethod(Activity activity) {
        Method[] method = activity.getClass().getDeclaredMethods();
        for (final Method m : method) {
            // 判断方法上面使用存在OnClick注解
            if (m.isAnnotationPresent(OnClick.class)) {
                // 如果存在则转换
                OnClick on = m.getAnnotation(OnClick.class);
                int[] values = on.value();// 获取需要绑定点击事件的控件id
                for (int i = 0; i < values.length; i++) {
                    final View view = activity.findViewById(values[i]);
                    // 给其设置点击事件
                    view.setOnClickListener(v -> {
                        try {
                            m.invoke(activity, view);// 调用activity里面的方法
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    });
                }
            }
        }
    }

使用的时候只需要在Activity中添加下面代码即可

    @OnClick({R.id.username, R.id.password})
    public void OnClick(View view) {
        switch (view.getId()) {
            case R.id.username:
                Toast.makeText(getApplicationContext(), "username", Toast.LENGTH_SHORT).show();
                break;
            case R.id.password:
                Toast.makeText(getApplicationContext(), "password", Toast.LENGTH_SHORT).show();
                break;
        }
    }

然后在onCreate方法中添加注解方法

ViewUtil.injectMethod(this);

上面的注解都是通过反射的方式来实现功能。我们知道反射的效率比较低,所以现在一些第三方的框架虽然也是用注解,但基本上不再使用反射了,他们会使用一个叫AbstractProcessor的类,这个类是抽象类,具体代码要自己实现,他会帮助我们在编译的时候自动生成一些代码,也就是我们添加注解的代码,然后在运行的时候直接调用,不需要在通过反射的方式,所以性能上也会有一定的提高。

posted @ 2019-05-22 15:42  数据结构和算法  阅读(85)  评论(0编辑  收藏  举报