内容简介:编码时使用注解,可以提高编码效率、简化代码增强可读性等优点;使用注解还是代码静态扫描的一部分,促进代码规范。安卓注解使用介绍一文中介绍了JDK/SDK提供的注解和support/ButterKnife等第三方提供的注解库,还有其他的一些库,这些基本已经能够满足需求。support/ButterKnife是应用很广的注解库,它们也是属于“自定义注解”的范畴,只是有因为使用的多了,实际上成为了一个“标准”。本文从“造库”的角度介绍自定义注解的相关支持,并提供一个示例实现。但是,本文不提供自定义注解相关的静态检查
编码时使用注解,可以提高编码效率、简化代码增强可读性等优点;使用注解还是代码静态扫描的一部分,促进代码规范。安卓注解使用介绍一文中介绍了JDK/SDK提供的注解和support/ButterKnife等第三方提供的注解库,还有其他的一些库,这些基本已经能够满足需求。
support/ButterKnife是应用很广的注解库,它们也是属于“自定义注解”的范畴,只是有因为使用的多了,实际上成为了一个“标准”。
本文从“造库”的角度介绍自定义注解的相关支持,并提供一个示例实现。但是,本文不提供自定义注解相关的静态检查, 这需要lint的支持 ,本文不做介绍,希望后面的文章有机会介绍一下, 这里先占个坑 。
第三方注解库
引入一个注解库,以ButterKnife为例:
- 添加注解库
implementation 'com.jakewharton:butterknife:8.4.0' 复制代码
- 添加注解处理器
annotationProcessor 'com.jakewharton:butterknife:8.4.0' 复制代码
添加了这两个库之后,就可以使用这个注解库了。
【 如果是library项目 】,还需要引入butterknife-gradle-plugin插件,在安卓注解使用介绍中有具体介绍。
定义注解
所有的注解都默认继承自 java.lang.annotation.Annotation
定义注解时可以声明0..N个成员,例如下面的定义,可以用 default 为成员指定默认值;成员名称可以按照程序语言的变量命名规则任意给定,成员的类型也是有限制的。在使用时需要指定参数名:@StringAnnotation(value = "data"),当成员只有一个且命名为value时,可省略。
8中基本数据类型,String,Class,Annotation及子类,枚举;
上面列举类型的数组,例如:String[]
public @interface StringAnnotation /*extends Annotation*/{ String value() default ""; } 复制代码
动态注解和静态注解
注解要在解析后才能最终发挥作用,解析过程有上面提到的 注解处理器 完成。依据注解处理器解析过程执行的时机,注解可以分为动态注解和静态注解。
动态注解
动态注解又叫运行时注解,注解的解析过程在执行期间进行,使用反射机制完成解析过程,会影响性能;
@Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface DynamicIntentKey { String value() default ""; } 复制代码
public class DynamicUtil { public static void inject(Activity activity) { Intent intent = activity.getIntent(); // 反射 for (Field field : activity.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(DynamicIntentKey.class)) { // 获取注解 DynamicIntentKey annotation = field.getAnnotation(DynamicIntentKey.class); String intentKey = annotation.value(); // 读取实际的IntentExtra值 Serializable serializable = intent.getSerializableExtra(intentKey); if (serializable == null) { if (field.getType().isAssignableFrom(String.class)) { serializable = ""; } } try { // 插入值 boolean accessible = field.isAccessible(); field.setAccessible(true); field.set(activity, serializable); field.setAccessible(accessible); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } 复制代码
静态注解
静态注解出现在动态注解之后,并取代动态注解。静态注解相对于动态注解,把注解的解释过程放在编译阶段,在运行时不再需要解释,而是直接使用编译的结果。
因此,编译阶段需要使用相应的 工具 生成所需的代码。
- 先定义一个注解
@Target(ElementType.FIELD) @Retention(RetentionPolicy.SOURCE) public @interface StaticIntentKey { String value(); } 复制代码
- 然后为这个注解定义一个处理器
注解解释器需要继承自AbstractProcessor基类,并使用@AutoService(Processor.class)声明这个类是一个注解处理器。
import com.google.auto.service.AutoService; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.Processor; @AutoService(Processor.class) public class StaticIntentProcessor extends AbstractProcessor { } 复制代码
public abstract class AbstractProcessor implements Processor { } 复制代码
- 注解处理器基类AbstractProcessor实现自Processor接口,其中init()和getSupportedOptions()在抽象类AbstractProcessor给出了实现,StaticIntentProcessor的主体功能是实现process()方法,完成类生成。
public interface Processor { Set<String> getSupportedOptions(); // 支持的注解类的类名集合 Set<String> getSupportedAnnotationTypes(); // 支持的 Java 版本 SourceVersion getSupportedSourceVersion(); void init(ProcessingEnvironment var1); boolean process(Set<? extends TypeElement> var1, RoundEnvironment var2); Iterable<? extends Completion> getCompletions(Element var1, AnnotationMirror var2, ExecutableElement var3, String var4); } 复制代码
- 通过下面的注解处理器,为所有使用了这个注解的类生成处理代码,不再需要运行时通过反射获得。
因为这个实现没有专门实现一个对应的android-library类型的工程,所以在使用这个注解时, 需要先编译完成 ,编译完成之后有了对应的注解处理器,才可以在Android工程中使用。
@AutoService(Processor.class) public class StaticIntentProcessor extends AbstractProcessor { private TypeName activityClassName = ClassName.get("android.app", "Activity").withoutAnnotations(); private TypeName intentClassName = ClassName.get("android.content", "Intent").withoutAnnotations(); @Override public SourceVersion getSupportedSourceVersion() { // 支持java1.7 return SourceVersion.RELEASE_7; } @Override public Set<String> getSupportedAnnotationTypes() { // 只处理 StaticIntentKey 注解 return Collections.singleton(StaticIntentKey.class.getCanonicalName()); } @Override public boolean process(Set<? extends TypeElement> set, RoundEnvironment re) { // StaticMapper的bind方法 MethodSpec.Builder method = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addParameter(activityClassName, "activity"); // 查找所有的需要注入的类描述 List<InjectDesc> injectDescs = findInjectDesc(set, re); for (int i1 = 0; i1 < injectDescs.size(); i1++) { InjectDesc injectDesc = injectDescs.get(i1); // 创建需要注解的类的Java文件,如上面所述的 IntentActivity$Binder TypeName injectedType = createInjectClassFile(injectDesc); TypeName activityName = typeName(injectDesc.activityName); // $T导入类型 // 生成绑定分发的代码 method.addCode((i1 == 0 ? "" : " else ") + "if (activity instanceof $T) {\n", activityName); method.addCode("\t$T binder = new $T();\n", injectedType, injectedType); method.addCode("\tbinder.bind((" + activityName + ") activity);\n", activityName, activityName); method.addCode("}"); } // 创建StaticMapper类 createJavaFile("com.campusboy.annotationtest", "StaticMapper", method.build()); return false; } private List<InjectDesc> findInjectDesc(Set<? extends TypeElement> set, RoundEnvironment re) { Map<TypeElement, List<String[]>> targetClassMap = new HashMap<>(); // 先获取所有被StaticIntentKey标示的元素 Set<? extends Element> elements = re.getElementsAnnotatedWith(StaticIntentKey.class); for (Element element : elements) { // 只关心类别是属性的元素 if (element.getKind() != ElementKind.FIELD) { processingEnv.getMessager().printMessage(Diagnostic.Kind.ERROR, "only support field"); continue; } // 此处找到的是类的描述类型 // 因为我们的StaticIntentKey的注解描述是field,所以closingElement元素是类 TypeElement classType = (TypeElement) element.getEnclosingElement(); System.out.println(classType); // 对类做缓存,避免重复 List<String[]> nameList = targetClassMap.get(classType); if (nameList == null) { nameList = new ArrayList<>(); targetClassMap.put(classType, nameList); } // 被注解的值,如staticName String fieldName = element.getSimpleName().toString(); // 被注解的值的类型,如String,int String fieldTypeName = element.asType().toString(); // 注解本身的值,如key_name String intentName = element.getAnnotation(StaticIntentKey.class).value(); String[] names = new String[]{fieldName, fieldTypeName, intentName}; nameList.add(names); } List<InjectDesc> injectDescList = new ArrayList<>(targetClassMap.size()); for (Map.Entry<TypeElement, List<String[]>> entry : targetClassMap.entrySet()) { String className = entry.getKey().getQualifiedName().toString(); System.out.println(className); // 封装成自定义的描述符 InjectDesc injectDesc = new InjectDesc(); injectDesc.activityName = className; List<String[]> value = entry.getValue(); injectDesc.fieldNames = new String[value.size()]; injectDesc.fieldTypeNames = new String[value.size()]; injectDesc.intentNames = new String[value.size()]; for (int i = 0; i < value.size(); i++) { String[] names = value.get(i); injectDesc.fieldNames[i] = names[0]; injectDesc.fieldTypeNames[i] = names[1]; injectDesc.intentNames[i] = names[2]; } injectDescList.add(injectDesc); } return injectDescList; } private void createJavaFile(String pkg, String classShortName, MethodSpec... method) { TypeSpec.Builder builder = TypeSpec.classBuilder(classShortName) .addModifiers(Modifier.PUBLIC, Modifier.FINAL); for (MethodSpec spec : method) { builder.addMethod(spec); } TypeSpec clazzType = builder.build(); try { JavaFile javaFile = JavaFile.builder(pkg, clazzType) .addFileComment(" This codes are generated automatically. Do not modify!") .indent(" ") .build(); // write to file javaFile.writeTo(processingEnv.getFiler()); } catch (IOException e) { e.printStackTrace(); } } private TypeName createInjectClassFile(InjectDesc injectDesc) { ClassName activityName = className(injectDesc.activityName); ClassName injectedClass = ClassName.get(activityName.packageName(), activityName.simpleName() + "$Binder"); MethodSpec.Builder method = MethodSpec.methodBuilder("bind") .addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL) .addParameter(activityName, "activity"); // $T导入作为类,$N导入作为纯值,$S导入作为字符串 method.addStatement("$T intent = activity.getIntent()", intentClassName); for (int i = 0; i < injectDesc.fieldNames.length; i++) { TypeName fieldTypeName = typeName(injectDesc.fieldTypeNames[i]); method.addCode("if (intent.hasExtra($S)) {\n", injectDesc.intentNames[i]); method.addCode("\tactivity.$N = ($T) intent.getSerializableExtra($S);\n", injectDesc.fieldNames[i], fieldTypeName, injectDesc.intentNames[i]); method.addCode("}\n"); } // 生成最终的XXX$Binder文件 createJavaFile(injectedClass.packageName(), injectedClass.simpleName(), method.build()); return injectedClass; } private TypeName typeName(String className) { return className(className).withoutAnnotations(); } private ClassName className(String className) { // 基础类型描述符 if (className.indexOf(".") <= 0) { switch (className) { case "byte": return ClassName.get("java.lang", "Byte"); case "short": return ClassName.get("java.lang", "Short"); case "int": return ClassName.get("java.lang", "Integer"); case "long": return ClassName.get("java.lang", "Long"); case "float": return ClassName.get("java.lang", "Float"); case "double": return ClassName.get("java.lang", "Double"); case "boolean": return ClassName.get("java.lang", "Boolean"); case "char": return ClassName.get("java.lang", "Character"); default: } } // 手动解析 java.lang.String,分成java.lang的包名和String的类名 String packageD = className.substring(0, className.lastIndexOf('.')); String name = className.substring(className.lastIndexOf('.') + 1); return ClassName.get(packageD, name); } private static class InjectDesc { private String activityName; private String[] fieldNames; private String[] fieldTypeNames; private String[] intentNames; @Override public String toString() { return "InjectDesc{" + "activityName='" + activityName + '\'' + ", fieldNames=" + Arrays.toString(fieldNames) + ", intentNames=" + Arrays.toString(intentNames) + '}'; } } } 复制代码
以上所述就是小编给大家介绍的《安卓自定义注解支持和示例实现》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Spring 注解编程之模式注解
- Java注解之编译时注解
- Java注解之运行时注解
- Java中的注解-自定义注解
- Java注解Annotation与自定义注解详解
- Java 元注解及 Spring 组合注解应用
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。