Android自定义注解
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); }