注解

2021-05-30  本文已影响0人  壹元伍角叁分

一:什么是注解?
注解只是个标签,本身并没有意义,需要结合其他场景使用
1、元注解:

//注解作用的地方:类、方法等,可以使用多个
@Target({ElementType.TYPE, ElementType.FIELD})

//注解保留的时间,三种:
//SOURCE(仅仅保留在源码阶段,被编译器忽略),
//CLASS(被编译器保留、但jvm会忽略),
//RUNTIME(jvm保留,运行时可以使用,通过反射)
@Retention(RetentionPolicy.SOURCE)

//语法检查:IDE或者IDE插件实现检查
@IntDef

2、如果只有一个属性,且名称为value,则使用时可以不用表明名称

@Target({ElementType.TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.SOURCE)
public @interface Lance {
    String value();//如有多个属性,或者名称不为value,则必须注明,否则报错
}

使用:

@Lance("value")
public class MainActivity extends AppCompatActivity {
}

3、可以设定默认值;如未设定,使用时,必须设置

public @interface Lance {
 int index() default 1;
}

使用:

@Lance //不用传参,使用默认值
public class MainActivity extends AppCompatActivity {
}

二、注解的应用场景

级别 技术 说明
源码 APT 在编译器能够获取注解与注解声明的类,包括类中所有成员信息,一般用于生产额外的辅助类
字节码 字节码增强 在编译出class后,通过修改class数据以实现修改代码逻辑目的。对于是否需要修改的区分或者修改为不同逻辑的判断可以使用注解
运行时 反射 在程序运行期间,通过反射技术东台获取注解及其元素,从而完成不同的逻辑判定

1、RetentionPolicy.SOURCE(源码)
APT:Annotation Processor Tools(注解处理程序)
javac收集到所有的注解信息,打包成一个节点(Element),然后调用APT进行

实际使用:
某个方法需要限定方法传入的类型,之前是用枚举实现,但枚举的使用,在生成class文件时,实际上是生成了若干个对象,而一个对象由对象头+成员组成,至少12个字节,比较占用内存。如果需要对应用运行内存进行一个优化,可以考虑使用常量+注解代替枚举类型。

public class InitialPenSize {
    @InitialPenSize
    private int mInitialPenSize;

    public static final int FINE = 0;//细,1f,
    public static final int NORMAL = 1;//正常,2.5f,
    public static final int THICK = 2;//粗, 5f,
    public static final int SUPER_THICK = 3;//超粗,9f

    @Target({ElementType.FIELD, ElementType.PARAMETER})
    @IntDef({FINE, NORMAL, THICK, SUPER_THICK})
    @Retention(RetentionPolicy.SOURCE)
    public @interface InitialPenSize {
    }

    public void setInitialPenSize(@InitialPenSize int penSize) {//限定了传入的类型
        this.mInitialPenSize = penSize;
    }
}

使用:

InitialPenSize initialPenSize = new InitialPenSize();
initialPenSize.setInitialPenSize(InitialPenSize.FINE);

2、RetentionPolicy.CLASS(字节码)
字节码增强,在字节码中写代码

3、RetentionPolicy.RUNTIME(运行时)
(1)注解+反射完成findViewById,Butter Knife原来的实现方式:

class InjectViewHelper {
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)
    @interface InjectView {
        @IdRes int value();
    }

    public static void inject(Activity activity) {
        Class<? extends Activity> aClass = activity.getClass();
        //Field[] fields = aClass.getFields();//获取自己及其父类的所有成员(不包括private,只能是public)
        Field[] declaredFields = aClass.getDeclaredFields();//获取自己的成员(包括private,不包括父类的)

        //那怎么获取到父类的private成员属性呢?
        //Field[] parentDeclaredFields = aClass.getSuperclass().getDeclaredFields();

        for (Field field : declaredFields) {
            if (field.isAnnotationPresent(InjectView.class)) {//判断属性是否被InjectView注解声明
                InjectView annotation = field.getAnnotation(InjectView.class);
                int valueId = annotation.value();//获取到注解上设置的值

                View viewById = activity.findViewById(valueId);
                field.setAccessible(true);//设置访问权限,允许操作private属性
                try {
                    field.set(activity, viewById);//赋值
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

使用:

public class MainActivity extends AppCompatActivity {
 @InjectViewHelper.InjectView(R.id.text_view)
    private TextView mTextView;

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

        InjectViewHelper.inject(this);//初始化
        mTextView.setText("设置成功");
}

(2)通过注解+反射实现页面跳转的参数注入

//如果页面跳转需要携带多种参数,则在SecondActivity中会获取多次
 StudentBean[] studentBeanArray = {new StudentBean(13), new StudentBean(14)};
 Intent intent = new Intent(MainActivity.this, SecondActivity.class)
                .putExtra("extra_string1", "extra1")
                .putExtra("extra_boolean", true)
                .putExtra("extraInt", 100)
                .putExtra("extra_student_bean", new StudentBean(10))
                .putParcelableArrayListExtra("extra_student_list", extraStudentBeanList)
                .putExtra("extra_student_array", studentBeanArray);
 startActivity(intent);
class InjectIntentHelper {
    @Target(ElementType.FIELD)
    @Retention(RetentionPolicy.RUNTIME)//运行时
    public @interface InjectIntent {
        String extraName() default "";//设置默认值,如果传入的key和变量名一样,可以不写
    }
    
    public static void inject(Activity activity) {
        Bundle extras = activity.getIntent().getExtras();
        Class<? extends Activity> aClass = activity.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field field : declaredFields) {
            boolean annotationPresent = field.isAnnotationPresent(InjectIntent.class);
            if (annotationPresent) {
                InjectIntent annotation = field.getAnnotation(InjectIntent.class);
                String extraName = annotation.extraName();

                //如果传入的key和变量名一样,且未声明,这边则去判断是否有值,没有则去获取变量名
                String extraKey = TextUtils.isEmpty(extraName) ? field.getName() : extraName;
                Object object = extras.get(extraKey);

                //这边需要注意:如果传入的类型是实现了Parcelable的对象数组,则需要特殊处理一下,其他类型不行
                Class<?> type = field.getType();
                Class<?> componentType = type.getComponentType();
                //判断是否是array数组并且是数组中对象实现了Parcelable接口
                if (type.isArray() && Parcelable.class.isAssignableFrom(componentType)) {
                    Object[] objectArray = (Object[]) object;
                    object = Arrays.copyOf(objectArray, objectArray.length, (Class<? extends Object[]>) type);
                }

                field.setAccessible(true);
                try {
                    field.set(activity, object);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  public static class SecondActivity extends AppCompatActivity {
        @InjectIntentHelper.InjectIntent(extraName = "extra_string1")
        private String extraString1;
        @InjectIntentHelper.InjectIntent(extraName = "extra_boolean")
        private boolean extraBoolean;
        @InjectIntentHelper.InjectIntent//这边可以省略不写
        private int extraInt;
        @InjectIntentHelper.InjectIntent(extraName = "extra_student_bean")
        private StudentBean extraStudentBean;
        @InjectIntentHelper.InjectIntent(extraName = "extra_student_list")
        private List<StudentBean> extraStudentList;
        @InjectIntentHelper.InjectIntent(extraName = "extra_student_array")
        private StudentBean[] extraStudentArray;

        @Override
        protected void onCreate(@Nullable Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);

            InjectIntentHelper.inject(this);//初始化
        }
    }

三、反射获取泛型的真实类型

上一篇下一篇

猜你喜欢

热点阅读