一:什么是注解?
注解只是个标签,本身并没有意义,需要结合其他场景使用
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);//初始化
}
}
三、反射获取泛型的真实类型
网友评论