Android annotation
注解是代码里的特殊标记,这些标记可以在编译、类加载、运行时被读取,并执行相应的处理。
在运行时读取需要使用Java反射机制进行处理。
一、注解的作用:
注解将一些本来重复性的工作,变成程序自动完成,简化和自动化该过程。比如用于生成Java doc,比如编译时进行格式检查,比如自动生成代码等,用于提升软件的质量和提高软件的生产效率。
二、注解都有哪些
1、Java提供了四种元注解,专门负责新注解的创建工作,即注解其他注解。
@Target
定义了Annotation所修饰的对象范围,取值:
ElementType.CONSTRUCTOR:用于描述构造器
ElementType.FIELD:用于描述域
ElementType.LOCAL_VARIABLE:用于描述局部变量
ElementType.METHOD:用于描述方法
ElementType.PACKAGE:用于描述包
ElementType.PARAMETER:用于描述参数
ElementType.TYPE:用于描述类、接口(包括注解类型) 或enum声明
@Retention
定义了该Annotation被保留的时间长短,取值:
- RetentionPoicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;用于做一些检查性的操作,比如 @Override 和 @SuppressWarnings
- RetentionPoicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期;用于在编译时进行一些预处理操作,比如生成一些辅助代码(如 ButterKnife)
- RetentionPoicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;用于在运行时去动态获取注解信息。
@Documented
标记注解,用于描述其它类型的注解应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化,不用赋值。
@Inherited
标记注解,允许子类继承父类的注解。 这里一开始有点理解不了,需要断句一下,允许子类继承父类的注解。
2、Android SDK内置的注解
Android SDK 内置的注解都在包com.android.support:support-annotations里,下面以'com.android.support:support-annotations:25.2.0'为例
资源引用限制类:用于限制参数必须为对应的资源类型
@AnimRes @AnyRes @ArrayRes @AttrRes @BoolRes @ColorRes等
线程执行限制类:用于限制方法或者类必须在指定的线程执行
@AnyThread @BinderThread @MainThread @UiThread @WorkerThread
参数为空性限制类:用于限制参数是否可以为空
@NonNull @Nullable
类型范围限制类:用于限制标注值的值范围
@FloatRang @IntRange
类型定义类:用于限制定义的注解的取值集合
@IntDef @StringDef
其他的功能性注解:
@CallSuper @CheckResult @ColorInt @Dimension @Keep @Px @RequiresApi @RequiresPermission @RestrictTo @Size @VisibleForTesting
3、自定义注解。。。。
三、如何使用Android Annotation
build.gradle
dependencies {
androidTestCompile('com.android.support:support-annotations:26.1.0') {
force = true
}
}
举例子:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BtnOnClick {
int[] value();
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface LayoutInject {
int value() default -1;
}
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
int value();
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewById {
int value();
}
public class AnnotationParse {
private static AnnotationParse instance;
public static AnnotationParse getInstance() {
if (instance == null) {
synchronized (AnnotationParse.class) {
if (instance == null) {
instance = new AnnotationParse();
return instance;
}
}
}
return instance;
}
public void bind(Activity activity) throws IllegalAccessException {
try {
injectLayout(activity);
bindView(activity);
bindOnClick(activity);
injectEvent(activity);
} catch (Exception ex) {
}
}
private static void bindView(Activity activity) throws IllegalAccessException {
//1、获取字节码
Class<? extends Activity> aClass = activity.getClass();
//2、获取Activity中变量
Field[] declaredField = aClass.getDeclaredFields();
for (Field field : declaredField) {
//设置允许暴力反射
field.setAccessible(true);
//3、获取变量上的注释
ViewById annotation = field.getAnnotation(ViewById.class);
if (annotation != null) {
//4、获取注释上的值
int id = annotation.value();
//5、通过ID获取控件
View view = activity.findViewById(id);
//6、控件赋值给变量
field.set(activity, view);
}
}
}
//对OnClick注解的“解释”
private static void bindOnClick(final Activity activity) {
//1、获取字节码对象
final Class<? extends Activity> aClass = activity.getClass();
//2、获取所有的方法
Method[] declaredMethods = aClass.getDeclaredMethods();
//3、遍历所有的方法
for (final Method method : declaredMethods) {
method.setAccessible(true);
//4、获取方法上的注释
OnClick annotation = method.getAnnotation(OnClick.class);
if (annotation != null) {
int id = annotation.value();
View view = activity.findViewById(id);
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
//执行注解的方法
method.invoke(activity, null);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
});
}
}
}
private static void injectEvent(final Activity activity) {
Class<? extends Activity> clazz = activity.getClass();
Method[] methods = clazz.getDeclaredMethods();
for (final Method method2 : methods) {
BtnOnClick click = method2.getAnnotation(BtnOnClick.class);
if (click != null) {
int[] viewId = click.value();
method2.setAccessible(true);
Object listener = Proxy.newProxyInstance(View.OnClickListener.class.getClassLoader(),
new Class[]{View.OnClickListener.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method2.invoke(activity, args);
}
});
try {
for (int id : viewId) {
View v = activity.findViewById(id);
Method setClickListener = v.getClass().getMethod("setOnClickListener", View.OnClickListener.class);
setClickListener.invoke(v, listener);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private static int mLayoutId = -1;
/**
* 注解布局Layout id
*/
private static void injectLayout(Activity activity) {
Class<?> clazz = activity.getClass();
if (clazz.getAnnotations() != null) {
if (clazz.isAnnotationPresent(LayoutInject.class)) {
LayoutInject inject = clazz.getAnnotation(LayoutInject.class);
mLayoutId = inject.value();
activity.setContentView(mLayoutId);
}
}
}
}
@LayoutInject(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@ViewById(R.id.tvName)
TextView tvName;
@ViewById(R.id.tvAge)
TextView tvAge;
@ViewById(R.id.tvGender)
TextView tvGender;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.activity_main);
try {
AnnotationParse.getInstance().bind(this);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
tvName.setText("测试名字");
tvAge.setText("28");
tvGender.setText("男");
}
@BtnOnClick({R.id.tvName, R.id.tvAge, R.id.tvGender})
public void btnOnClick(View view) {
switch (view.getId()) {
case R.id.tvName:
tvName.setText("华山论剑");
break;
case R.id.tvAge:
tvAge.setText("四百多岁");
break;
case R.id.tvGender:
tvGender.setText("女");
break;
}
}
@OnClick(R.id.tvName)
public void textOnClick() {
tvName.setText("华山论剑");
}
}

浙公网安备 33010602011771号