本文是应用在Android环境上的,示例也以Android示例为主;其次,Android先在已经有了官方AnnotationProcessor编译时解析实现,已继承在API中,由于它是编译时执行,固然不会出现反射中的性能消耗; 该文中示例采用比较彻底的注解完成了咱们平时使用的activity中 引入layout布局和findViewById方法 的使用
反射还不太了解的童鞋,你可以先了解以下 java反射
一、什么是注解
注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释;
java元注解:(元注解,简单理解,就是注解上的注解)
@Override 它的作用是对覆盖超类中方法的方法进行标记
@Deprecated 它的作用是对不应该再使用的方法添加注解
@SuppressWarnings 它的作用是对当前方法或字段异常时的警告
二、简单使用注解
2.1 了解JDK1.5后加的内部注解
//基类
public abstract class Base {
public abstract void one();
@Deprecated //表示该方法为过时方法,过时不再支持使用
public void two(){};
}
//继承
public class Base2 extends Base {
@SuppressWarnings("deprecation")
@Override //表示该方法时继承父类的方法
public void one() {
//当调备注"过时"标签时,系统会提示有警报,然后调用SuppressWarnings可以取消工具上的报警状态
super.two();
}
}
下图为没加SuppressWarnings标签的样子
2.2 自定义注解的简单使用
package annot;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解
* @author mwt
*
* 注意了,自定义注解的标志是“@interface”,要和接口interface区分开噢
*/
//如下代码,示例中的@Retention指定了什么时候加载注解类,RetentionPolicy.RUNTIME为运行时
@Retention(RetentionPolicy.RUNTIME)
//@Target指定当前注解可以加在哪些地方,案例中就是加载到类和方法
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Test1 {
//default 表示整型id默认值为123
public int id() default 123;
// 字符串类型
public String message() default "默认值";
}
//-----------------------注解使用-------------------------------
package annot;
/**
* 对自定义注解的使用
* @author mwt
*/
@Test1(message="自定义注解开始使用了") //改变单个属性值
public class TestUse {
public static void main(String[] args) {
//改行代码是用来判断当前 “TestUse”类是否加了Test1注解
boolean hasAnnotation = TestUse.class.isAnnotationPresent(Test1.class);
//经过判断如果加了注解,则进入下一步
if (hasAnnotation) {
//该行代码是用来获取当前注解
Test1 testAnnotation = TestUse.class.getAnnotation(Test1.class);
//获取当前注解中的属性
System.out.println("value:"+testAnnotation.id()+" \nmessage"+testAnnotation.message());
}
}
}
//----------------日志输出
value:123
message自定义注解开始使用了
三、在Android中简单使用注解(基于反射)
3.1 首先我们先完成两个自定义注解;分别是对layout和view的注解
package cn.annto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解 - layout
* Created by mwt on 2018/9/26.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface LayoutBind {
//当只有一个属性的时候,可以使用value来表示,在使用时可以省略掉key和=
public int value();
}
//---------------------------view 注解----------------------
package cn.annto;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解 -
* Created by mwt on 2018/9/26.
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
public @interface ViewBind {
//当只有一个属性的时候,可以使用value来表示,在使用时可以省略掉key和=
public int value();
}
3.2 写一个解读activity中注解的工具类
package cn.annto;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import java.lang.reflect.Field;
/**
* Created by mwt on 2018/9/26.
* 为什么要写成抽象类呢,因为我可以强制性继承类重写该方法
*/
public abstract class ActivityUntil extends AppCompatActivity {
private int layoutId;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initLayout();
initView();
doWrok();
}
protected abstract void doWrok();
/**
* 该方法主要就是从注解中获取到注解入的值
*/
private void initLayout() {
//首先判断一下是否属于当前注解的子类 ;如果是提供给别人使用的话,务必要有判空操作
boolean isannotation = this.getClass().isAnnotationPresent(LayoutBind.class);
if (isannotation){
//获取当前的注解
LayoutBind annotation = getClass().getAnnotation(LayoutBind.class);
//获取注解中的值,并将该值赋给全局变量layoutid,然后供setContentView使用
layoutId = annotation.value();
//吧layout布局引入
setContentView(layoutId);
}
}
/**
* 初始化activity中生命的字段
*/
private void initView() {
//获取当前class对象
Class<? extends ActivityUntil> aClass = this.getClass();
//获取到该class中自定义的所有字段
Field[] declaredFields = aClass.getDeclaredFields();
//便利所有的字段
for (Field field: declaredFields) {
//所有的字段便利出来了,但是我们只需要那些被加过注解的字段
if(null != field.getAnnotations() && field.isAnnotationPresent(ViewBind.class)){
field.setAccessible(true);//所有字段都可访问修改
//获取字段上的注解类
ViewBind viewBind = field.getAnnotation(ViewBind.class);
//获取该字段通过注解注入 值,也就是 R.id.textview .....
int viewid = viewBind.value();
//现在字段获取到了,对应view 的id也获取到了,那就应该赋值了
try {
field.set(this,findViewById(viewid));
} catch (IllegalAccessException e) {
e.printStackTrace();
Log.e("error","not find view id");
}
}
}
}
}
3.3 自定义注解和解读工具类都具备了,让我们来使用一把吧
package cn.annto;
import android.widget.TextView;
@LayoutBind(R.layout.activity_main)
public class MainActivity extends ActivityUntil {
@ViewBind(R.id.one)
public TextView oneText;
@ViewBind(R.id.two)
public TextView twoText;
@Override
protected void doWrok() {
oneText.setText("注解测试");
twoText.setText("注解通过了");
}
}
运行效果:
本文中第三节Android demo 在github上有完整示例,https://github.com/mawantong/annto/tree/master