1、注解的定义与使用场景
注解本身没意义,单独就是一种注释,需要结合反射、插桩才有意义
元注解:注解上的注解
target限制注解的地方
ElementType.ANNOTATION_TYPE 可以应用于注解类型。
ElementType.CONSTRUCTOR 可以应用于构造函数。
ElementType.FIELD 可以应用于字段或属性。
ElementType.LOCAL_VARIABLE 可以应用于局部变量。
ElementType.METHOD 可以应用于方法级注解。
ElementType.PACKAGE 可以应用于包声明。
ElementType.PARAMETER 可以应用于方法的参数。
ElementType.TYPE 可以应用于类的任何元素。
retention 保留级别 (保留注解到什么时候)
RetentionPolicy.SOURCE 源码
保留在源码里面,编译成class,就没有注解了
应用:IDE语法检查 IntDef
APT注解处理器技术使用 自动生成一些辅助类 Arouter
.java -> javac -> .class
javac 过程中执行 ,生成class之前
采集到所有的注解及注解声明类的信息 -> Element ->注解处理程序
RetentionPolicy.CLASS 保留在编译期,jvm里面忽略注解 字节码
字节码增强:字节码中写代码 (热修复)Butterknife
RetentionPolicy.RUNTIME 一直保留到运行期(可以结合反射获取) 运行时
2、反射与Type体系
反射就是不知道初始化的类对象是什么,不能new。在运行状态中,对于任意一个类,都能够知道、调用、修改这个类的所有属性和调用方法。
功能:
在运行时构造任意一个类的对象
在运行时获取或者修改任意一个类所具有的成员变量
在运行时获取、调用任意一个对象的方法
3、远古框架常用技术,反射与注解结合实现View的注入
反射获取泛型
TypeVariable
泛型类型变量。可以泛型上下限等信息;
ParameterizedType
具体的泛型类型,可以获得元数据中泛型签名类型(泛型真实类型)
GenericArrayType
当需要描述的类型是泛型类的数组时,比如List[],Map[],此接口会作为Type的实现。
WildcardType
通配符泛型,获得上下限信息
Gson gson = new Gson();
Base<Data> base=gson.fromJson(json,new TypeToken<Base<Data>>(){}.getType());
newTypeToken创建了一个匿名内部类,因为构造器是protected,这样可以.class获取Base<T> 的泛型
Filed:自己和父类的成员(public)
DeclaredFiled:只能获取自己的成员 (所有的)
Method:自己和父类的方法(public)
DeclaredMethod;只能获取自己的方法 (所有的)
ButterKnife注解实现
@InjectView(R.id.tv)
TextView tv;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface InjectView {
@IdRes int value();
}
public class InjectUtil {
public static void injectView(Activity activity){
Class<? extends Activity> aClass = activity.getClass();
Field[] declaredFields = aClass.getDeclaredFields();
for (Field field : declaredFields) {
//判断是不是带注解标识
if(field.isAnnotationPresent(InjectView.class)) {
InjectView injectView = field.getAnnotation(InjectView.class);
int value = injectView.value();
View view = activity.findViewById(value);
//反射设置属性的值
field.setAccessible(true);//设置访问权限,允许操作private的属性
try {
//反射赋值
field.set(activity,view);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
注解与反射实现页面跳转参数传递(项目中不建议这样使用,反射遍历所有成员,会影响性能)
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoIntent {
String value() default "";
}
@AutoIntent
private User user;
@AutoIntent
private String name;
@AutoIntent
private int age;
@AutoIntent
private List<User> userList;
public static void autoIntentExtra(Activity activity){
Class<? extends Activity> cls = activity.getClass();
Intent intent = activity.getIntent();
Bundle extras = intent.getExtras();
if(extras==null) {
return;
}
Field[] declaredFields = cls.getDeclaredFields();
for (Field field : declaredFields) {
if(field.isAnnotationPresent(AutoIntent.class)) {
AutoIntent autoIntent = field.getAnnotation(AutoIntent.class);
String key= TextUtils.isEmpty(autoIntent.value())?field.getName():autoIntent.value();
if(extras.containsKey(key)) {
Object object = extras.get(key);
// todo Parcelable数组类型不能直接设置,其他的都可以.
//获得数组单个元素类型
Class<?> componentType = field.getType().getComponentType();
//当前属性是数组并且是 Parcelable(子类)数组
if (field.getType().isArray() &&
Parcelable.class.isAssignableFrom(componentType)) {
Object[] objs = (Object[]) object;
//创建对应类型的数组并由objs拷贝
Object[] objects = Arrays.copyOf(objs, objs.length, (Class<? extends Object[]>) field.getType());
object = objects;
}
field.setAccessible(true);
try {
field.set(activity,object);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}