Android自定义注解

时间:2022-02-18 16:51:45
1、元注解
 
概念:用来定义其他注解的注解,自定义注解的时候,需要使用它来定义我们的注解。
 
在jdk 1.5之后提供了 java.lang.annotation 来支持注解功能
 
常见的四种元注解有 :
@Target (目标,  注解可以使用的地方,参数是一个ElementType 枚举)
@Retention  (保持性, 描述注解的生命周期  ) 
@Inherited ( 可继承的, 参数true or false ,表示是否允许子类继承该注解,默认false)
@Document (文档化,表明注解可以被javadoc 此类工具文档化)
 
1.1 @Target
@Target ElementType 枚举类型
 
ElementType.Type
接口、类、注解、枚举
ElementType.FIELD
字段、枚举常量
ElementType.METHOD
方法
ElementType.PARAMETER
方法参数
ElementType.CONSTRUCOTOR
构造函数
ElementType.LOCAL_VARIABLE
局部变量
ElementType.ANNOTATION_TYPE
注解
Element.PACKAGE
1.2 @Retention
 
 用于描述注解的生命周期, 注解在什么地方使用有效
参数 RetentionPolicy 枚举对象
 
RetentionPolicy.SOURCE
源文件,当java文件被编译成class文件时,注解失效
RetentionPolicy.CLASS
注解存在class 文件,当jvm 加载class文件时,注解生效,默认指定的参数
RetentionPolicy.RUNTIME
注解保存到class文件,jvm加载class文件后,依然有效
周期有效性, RUNTIME > CLASS > SOURCE
 
1.3 @Document
  标记自定义注解可被javadoc 此类文档化
 
1.4 @Inherited
 @Inherited    表明我们标记的注解是被继承的,如果一个父类使用@Inherited 修饰的注解,则允许子类继承该父类的注解
 
二、自定义注解
 
步骤:
1、申明注解,确定注解的运行生命周期、目标、参数
2、注解解析:找到被注解类的方法、属性。添加自定义注解的一些操作
 
案例1、注解创建对象
2.1申明注解AutoCreateObject
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* author: rexkell
* explain:
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface AutoCreateObject {
}

2.2解析注解

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

/**
* author: rexkell
* explain:
*/
public class AutoCreateProcess {
public static void bind(final Object object){
Class parentClass=object.getClass();
Field[] fields= parentClass.getFields();
for (Field field: fields){
AutoCreateObject autoCreateObject= field.getAnnotation(AutoCreateObject.class);
if (autoCreateObject!=null){
field.setAccessible(true);
try {
Class<?> autoCreateClass= field.getType();
Constructor autoCreateConstructor= autoCreateClass.getConstructor();
field.set(object,autoCreateConstructor.newInstance());
} catch (NoSuchMethodException e) {
e.printStackTrace();
}catch (IllegalAccessException e){
e.printStackTrace();
}catch (InvocationTargetException e){
e.printStackTrace();
}catch (InstantiationException e){
e.printStackTrace();
}

}
}
}
}
@AutoCreateObject
Students students;
//创建对象
AutoCreateProcess.bind(this);

3、模拟bindViewId

3.1、创建一个java Module 

implementation 'com.squareup:javapoet:1.9.0'
implementation 'com.google.auto.service:auto-service:1.0-rc2'

3.2 申明注解

import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* author: rexkell
* explain:
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
int value() default -1;
}

3.3 解析注解

import android.app.Activity;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

/**
* author: rexkell
* explain:
*/
public class MyBindView {
private static Map<Class, Method> classMethodMap=new HashMap<>();
public static void bind(Activity target){
if (target!=null){
Method method = classMethodMap.get(target.getClass());
try {
if (method==null){
//获取编译生成的注解类
String bindClassName= target.getPackageName()+".Bind"+target.getClass().getSimpleName();
Class bindClass=Class.forName(bindClassName);
method=bindClass.getMethod("bindView",target.getClass());
classMethodMap.put(target.getClass(),method);
}
method.invoke(null,target);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

由于是编译时产生的注解,需要通过 extends AbstractProcessor 来实现

import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeName;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.util.Elements;


/**
* author: rexkell
* explain:
*/
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
public class BindProcess extends AbstractProcessor {
private Elements mElementsUtil;
private Map<TypeElement,Set<Element>> mBindViewElems;

@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
mElementsUtil=processingEnv.getElementUtils();
mBindViewElems=new HashMap<>();

}

@Override
public Set<String> getSupportedAnnotationTypes() {
//添加需要解析的自定义注解类
Set<String> types=new HashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(BindLayout.class.getCanonicalName());
return types;
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("Process start!");
initBindElems(roundEnv.getElementsAnnotatedWith(BindView.class));
generateJavaClass();
System.out.println("Process finish!");
return true;
}
//初始化绑定的控件
private void initBindElems(Set<? extends Element> bindElems){
for (Element bindElem : bindElems){
TypeElement enclosedElem=(TypeElement) bindElem.getEnclosingElement();
Set<Element> elems=mBindViewElems.get(enclosedElem);
if (elems==null){
elems=new HashSet<>();
mBindViewElems.put(enclosedElem,elems);
System.out.println(enclosedElem.getSimpleName());
}
elems.add(bindElem);
System.out.println("Add bind elem "+bindElem.getSimpleName());
}
}
private void generateJavaClass(){
//生成Bind+ClassName+.class 文件,文件内容实现findViewById
for (TypeElement enclosedElem: mBindViewElems.keySet()){
MethodSpec.Builder methodSpesBuilder = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.addParameter(ClassName.get(enclosedElem.asType()),"activity")
.returns(TypeName.VOID);
BindLayout bindLayoutAnno =enclosedElem.getAnnotation(BindLayout.class);
if (bindLayoutAnno!=null){
methodSpesBuilder.addStatement(String.format(Locale.US,"activity.setContentView(%d)",bindLayoutAnno.value()));
}
for (Element bindElem : mBindViewElems.get(enclosedElem)){
methodSpesBuilder.addStatement(String.format(Locale.US,"activity.%s=(%s)activity.findViewById(%d)",
bindElem.getSimpleName(),bindElem.asType(),bindElem.getAnnotation(BindView.class).value()));
}
TypeSpec typeSpec=TypeSpec.classBuilder("Bind"+enclosedElem.getSimpleName())
.superclass(TypeName.get(enclosedElem.asType()))
.addModifiers(Modifier.FINAL,Modifier.PUBLIC)
.addMethod(methodSpesBuilder.build())
.build();
JavaFile file = JavaFile.builder(getPackageName(enclosedElem),typeSpec).build();
try {
file.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}

}
}
private String getPackageName(TypeElement typeElement){
return mElementsUtil.getPackageOf(typeElement).getQualifiedName().toString();
}


}

3.4 在需要使用bindViewId 注解中引入模块。

@BindView(R.id.edt_longitude)
 EditText edtLongitude;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SharedPreferences sharedPreferences= this.getSharedPreferences("theme",MODE_PRIVATE);
int themeId=sharedPreferences.getInt("themeId",2);
if (themeId==1){
setTheme(R.style.BaseAppThemeNight);
}else if (themeId==0){
setTheme(R.style.AppTheme);
}
setContentView(R.layout.activity_main);
MyBindView.bind(this);
}