注解可以为Java代码中添加信息,然后在某个时刻去运用它。Java内置了三种注解,分别为:
@Override,(表示当前方法覆盖超类的方法)
@Deprecated,(如果使用了含有此注解的元素,编译器将会警告)
@SuppressWarnings.(关闭编译器的警告信息)
一,定义注解
Java提供了四种注解来负责新注解的创建,分别是
@Tartget:
表示该注解用在什么地方,参数包括:
CONSTRUCTOR:构造器的声明
FIELD:域声明
LOCAL_VARIABLE:局部变量声明
METHOD:方法声明
PACKIAGE:包声明
TYPE:类,接口或enum声明
@Retention:
表示在哪个级别保存注解信息,参数包括:
SOURCE::注解会被编译器丢弃
CLASS:注解在class文件中可以用
RUNTIME:运行期保留注解,可以通过反射机制读取注解的信息
@Documented
将此注解包含在Javadoc中
@Inherited
允许子类继承父类的注解
例子:要定义在方法上一个运行时注解
@Tartget(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Test{
}
使用时如下:
class H{
@Test
void someMethod(){
}
}
在运行时,可以通过反射获取@Test的信息
二,注解处理
标记在代码的注解可以在运行时通过反射获取其信息
for(mothod m : H.getDeclaredMethods()){
Test test=m.getAnnotation(Test.class)
if(test != null){
//找到注解信息,进行处理
}
}
三,注解在Android中的应用
在Android开发中,经常会根据控件id获取控件实例,例如获取一个按钮实例,并设置点击事件
Button btn=(Button)findviewbyid(R.id.xxxx);
btn.setOnClickListener(xxx);
大部分情况下,编写一个Activity页面时,都会设置大量的按钮,那么都要写大量同样样式的代码,为什么不交给程序去完成这种繁复的劳动呢?
我们可以省掉写这些代码的时间,可以利用注解来解决。
首先,定义注解,按钮有两个基本作用,一是获取实例便于稍后做其它操作,二是设置点击事件,由此可以这样定义:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ButtonHandle {
public int id();
public boolean click() default false;
}
@Target标识该注解用于变量上,@Retention说明该注解用于运行时,注解名称为ButtonHandle,有两个属性,int类型属性id对于布局中的R.id.xxx
boolean类型属性click标识是否设置点击事件。使用如下:
@ButtonHandle(id = R.id.xxx, click =true)
private Button btn;
这个注解的作用是把id为R.id.xxx的控件实例赋值给btn,并设置btn的点击事件。
下面来实现这个注解的功能,需要写一个注解处理器,先写关键代码:
public static void ButtonHandle(Object activity, View sourceView) {
// 通过反射获取到全部属性
Field[] fields = activity.getClass().getDeclaredFields();
if(fields !=null&& fields.length >0) {
for(Field field : fields) {
// 返回ButtonHandle类型的注解内容
BindView bindView = field.getAnnotation(ButtonHandle.class);
if(bindView !=null) {
//获取标记在注解上的id
intviewId = bindView.id();
//是否设置点击事件
boolean clickLis = bindView.click();
try{
field.setAccessible(true);
if(clickLis) {
//注解帮我们完成的事,设置点击事件
sourceView.findViewById(viewId).setOnClickListener(
(OnClickListener) activity);
}
//注解帮我们完成的事,赋值
field.set(activity, sourceView.findViewById(viewId));
}catch(Exception e) {
e.printStackTrace();
}
}
}
}
}
上面的代码完成获取实例并给按钮设置点击事件, activity表示当前activity, sourceView表示activity的根view,即DecorView,在onCreate()里
调用以上方法即可完成注解的处理,给Btton设置点击事件。
四,编译时注解
运行时注解的处理用到了反射,假如在意反射损耗的性能,可以考虑使用编译时注解,可以利用编译时动态生成想要源代码,实现相关功能。
在Android studio 中新建一个java Library的Module,在里面实现一个注解处理器,该处理器继承自AbstractProcessor,重写getSupportedAnnotationTypes()和process()方法:
public classMyProcessorextendsAbstractProcessor {
privateFilerfiler;
@Override
public synchronized voidinit(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// Filer
filer= processingEnv.getFiler();
}
@Override
public booleanprocess(Set annotations,RoundEnvironment roundEnv) {
for(TypeElement element : annotations) {
if(element.getQualifiedName().toString().equals(MyAnnotation.class.getCanonicalName())) {
MethodSpec main = MethodSpec.methodBuilder("main")
// .addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class,"args")
.addStatement("$T.out.println($S)",System.class,"Hello, JavaPoet!")
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
// .addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build();
JavaFile javaFile = JavaFile.builder("com.example.helloworld",helloWorld)
.build();
try{
javaFile.writeTo(newFile(filer);
}catch(IOException e) {
e.printStackTrace();
}
}
}
return true;
}
@Override
publicSetgetSupportedAnnotationTypes() {
Set annotataions =newLinkedHashSet();
annotataions.add(MyAnnotation.class.getCanonicalName());
returnannotataions;
}
@Override
publicSourceVersiongetSupportedSourceVersion() {
returnSourceVersion.latestSupported();
}
新建一个注解的module,定义注解:
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public@interfaceMyAnnotation{
Stringvalue();
}
写完后好需要配置,在main文件夹下创建一个resources.META-INF.services文件夹,创建文件
javax.annotation.processing.Processor,文件内容是Process类的包名+类名。
然后在要使用注解的module依赖包含注解处理器的module和包含注解定义的module,并使用:
然后Build该Module。完成后,打开build文件夹,生成的代码在该文件夹下,可以直接利用其功能。