Android 自定义编译时注解1
来源:互联网 发布:有了源码怎么做软件 编辑:程序博客网 时间:2024/06/06 20:45
为什么要写这一系列的博客呢?
因为在 Android 开发的过程中, 泛型,反射,注解这些知识进场会用到,几乎所有的框架至少都会用到上面的一两种知识,如 Gson 就用到泛型,反射,注解,Retrofit 也用到泛型,反射,注解 。学好这些知识对我们进阶非常重要,尤其是阅读开源框架源码或者自己开发开源框架。
java Type 详解
java 反射机制详解
注解使用入门(一)
Android 自定义编译时注解1 - 简单的例子
Android 编译时注解 —— 语法详解
带你读懂 ButterKnife 的源码
前言
记得去年的时候写过一篇博客 注解使用入门(一),这篇博客主要介绍了注解的一些基本知识,以及基于运行时注解的 Demo。今天这篇博客主要介绍怎样编写编译时注解的Demo。
这篇博客代码参考了鸿洋的博客: Android 打造编译时注解解析框架 这只是一个开始
注解的重要知识
我们先复习一下注解的一些重要知识:
根据注解使用方法和用途,我们可以将Annotation分为三类:
- JDK内置系统注解,如 @Override 等
- 元注解
- 自定义注解,我们自己实现的自定义注解
元注解:
元注解的作用就是负责注解其他注解。Java5.0定义了4个标准的meta-annotation类型,它们被用来提供对其它annotation类型作说明。Java5.0定义的元注解:
1. @Target
2. @Retention
3. @Documented
4. @Inherited
元注解 解析说明
@Documented 是否会保存到 Javadoc 文档中
@Retention 保留时间,可选值
SOURCE(源码时),CLASS(编译时),RUNTIME(运行时)
默认为 CLASS,SOURCE 大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override, SuppressWarnings
@Target 可以用来修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未标注则表示可修饰所有
ANONOTATION_TYPE(注解类型声明),
PACKAGE(包)
TYPE (类,包括enum及接口,注解类型)
METHOD (方法)
CONSTRUCTOR (构造方法)
FIFLD (成员变量)
PARAMATER (参数)
LOCAL_VARIABLE (局部 变量)@Inherited 是否可以被继承,默认为 false
编译时注解例子说明
这里我们以 AndroidStudio 为例子讲解。假设我们要把 User 这样的一个类,在编译时转化成类似于 json 这样键值对的形式。大概需要三步。
public class Person { @Seriable() String name; @Seriable() String area; @Seriable() int age; int weight; @Seriable() List<Article> mArticleList;}
{class:"xj.jsonlibdemo.Person", fields: { name:"java.lang.String", area:"java.lang.String", age:"int", mArticleList:"java.util.List<xj.jsonlibdemo.Article>" }}
第一步:我们新建一个 java library,搭配好相关的配置,并编写我们自定义的 Animation Seriable,如下所示
首先:我们新建一个 java library:
接着: 编写我们的自定义注解
@Documented()// 表示是基于编译时注解的@Retention(RetentionPolicy.CLASS)// 表示可以作用于成员变量,类、接口@Target({ElementType.FIELD, ElementType.TYPE}) public @interface Seriable {}
如果对元注解还步了解的话,建议先阅读我之前写的博客 注解使用入门(一),这里不再讲解
最后:在 resources/META-INF/services/javax.annotation.processing.Processor 文件中 添加 我们自定义注解的全限定路径 com.example.JsonProcessor。注意若 resources/META-INF/services/javax.annotation.processing.Processor 不存在,需要自己添加。
第二步:编写我们的解析器,继承 AbstractProcessor ,并重写 process 方法,处理相关逻辑。
@SupportedAnnotationTypes({"com.example.Seriable"})@SupportedSourceVersion(SourceVersion.RELEASE_7)public class JsonProcessor extends AbstractProcessor { private Elements mElementUtils; @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); // 工具辅助类 mElementUtils = processingEnv.getElementUtils(); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { // 第一步,根据我们自定义的注解拿到 elememts set 集合 Set<? extends Element> elememts = roundEnv.getElementsAnnotatedWith(Seriable.class); TypeElement typeElement; VariableElement variableElement; Map<String, List<VariableElement>> map = new HashMap<>(); List<VariableElement> fileds = null; // 第二步: 根据 element 的类型做相应的处理,并存进 map 集合 for (Element element : elememts) { ElementKind kind = element.getKind(); // 判断该元素是否为类 if (kind == ElementKind.CLASS) { typeElement = (TypeElement) element; // 这里以类的全限定类名作为 key,确保唯一 String qualifiedName = typeElement.getQualifiedName().toString(); map.put(qualifiedName, fileds = new ArrayList<VariableElement>()); // 判断该元素是否为成员变量 } else if (kind == ElementKind.FIELD) { variableElement = (VariableElement) element; // 获取该元素的封装类型 typeElement = (TypeElement) variableElement.getEnclosingElement(); String qualifiedName = typeElement.getQualifiedName().toString(); fileds = map.get(qualifiedName); if (fileds == null) { map.put(qualifiedName, fileds = new ArrayList<VariableElement>()); } fileds.add(variableElement); } } Set<String> set = map.keySet(); for (String key : set) { if (map.get(key).size() == 0) { typeElement = mElementUtils.getTypeElement(key); List<? extends Element> allMembers = mElementUtils.getAllMembers(typeElement); if (allMembers.size() > 0) { map.get(key).addAll(ElementFilter.fieldsIn(allMembers)); } } } // 第三步:根据 map 集合数据生成代码 generateCodes(map); return true; } // 生成我们的代码文件 private void generateCodes(Map<String, List<VariableElement>> maps) { File dir = new File("f://Animation"); if (!dir.exists()) dir.mkdirs(); // 遍历map for (String key : maps.keySet()) { // 创建文件 File file = new File(dir, key.replaceAll("\\.", "_") + ".txt"); try { /** * 编写json文件内容 */ FileWriter fw = new FileWriter(file); fw.append("{").append("class:").append("\"" + key + "\"") .append(",\n "); fw.append("fields:\n {\n"); List<VariableElement> fields = maps.get(key); for (int i = 0; i < fields.size(); i++) { VariableElement field = fields.get(i); fw.append(" ").append(field.getSimpleName()).append(":") .append("\"" + field.asType().toString() + "\""); if (i < fields.size() - 1) { fw.append(","); fw.append("\n"); } } fw.append("\n }\n"); fw.append("}"); fw.flush(); fw.close(); } catch (IOException e) { e.printStackTrace(); } } }}
思路解析
- 第一步,根据我们自定义的注解拿到 elememts set 集合
- 第二步:根据 elememt 的类型做相应的处理,并存进 map 集合
- 第三步:根据 map 集合的数据,生成相应的代码。
第三步:调用 gradle build 命令生成 jar 包
在 AndroidStudio 中的 Terminal 窗口输入 gradle build 命令,完成之后将生成 jar 包。我们就可以使用这一个 jar 包了。需要注意的是 我们需要将 gradle 添加到环境变量中。
第四步,将我们生成的 jar 包复制到 moudle 目录下,compile fileTree(dir: ‘libs’, include: [‘*.jar’]) , 就可以使用了。
比如我们新建一个 moudle,新建两个类,如下:
@Seriablepublic class Article { private String title; private String content;}
public class User { @Seriable() String name; @Seriable() String area; @Seriable() int age; int weight; @Seriable() List<Article> mArticleList;}
在 moudle 的目录下执行 gradle build 命令,将可以在我们的保存路径中看到我们生成的两个文件,(这个路径是我们前面在编写 JsonProcessor 缩写的,File dir = new File(“f://Animation”);)
{class:"xj.jsonlibdemo.Article", fields: { title:"java.lang.String", content:"java.lang.String", time:"long" }}
{class:"xj.jsonlibdemo.Person", fields: { name:"java.lang.String", area:"java.lang.String", age:"int", mArticleList:"java.util.List<xj.jsonlibdemo.Article>" }}
到此,一个简单的例子讲解完毕:
参考博客:
Android 打造编译时注解解析框架 这只是一个开始
github 地址
- Android 自定义编译时注解1
- Android如何开发自定义编译时注解
- Android 编译时注解
- Android 编译时注解
- android编译时注解框架
- Android 编译时解析注解
- 自定义运行时注解、编译时注解[ButterKnife原理探析]
- 自定义注解之编译时注解(RetentionPolicy.CLASS)(一)
- 自定义注解之编译时注解(RetentionPolicy.CLASS)(一)
- Android编译时注解框架系列1-什么是编译时注解
- Android编译时注解框架系列1-什么是编译时注解
- Android中的自定义注解(反射实现-运行时注解)
- Android中的自定义注解(反射实现-运行时注解)
- Android中的自定义注解(反射实现-运行时注解)
- Android中的自定义注解(反射实现-运行时注解)
- Android中的自定义注解(反射实现-运行时注解)
- Android 自定义注解框架
- Android中的自定义注解
- 滑动窗口的最大值模拟实现
- 计算销售税
- 三种方法实现按钮的点击事件
- perl MQSeries::Queue sync方法
- Swoole 安装 使用
- Android 自定义编译时注解1
- 在 Linux 下学习 C 语言有什么好处?
- mysql数据库jdbc连接通用类
- zoj3954 Seven-Segment Display
- 概率图模型之:贝叶斯网络
- Isomorphic Strings
- Linux下jdk的安装
- 2D sprite outlines
- UE4场景中电视机播放视频,并产生辉光效果