




已阅读5页,还剩6页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
一小时搞明白注解处理器(Annotation Processor Tool)什么是注解处理器? 注解处理器是(Annotation Processor)是javac的一个工具,用来在编译时扫描和编译和处理注解(Annotation)。你可以自己定义注解和注解处理器去搞一些事情。一个注解处理器它以Java代码或者(编译过的字节码)作为输入,生成文件(通常是java文件)。这些生成的java文件不能修改,并且会同其手动编写的java代码一样会被javac编译。看到这里加上之前理解,应该明白大概的过程了,就是把标记了注解的类,变量等作为输入内容,经过注解处理器处理,生成想要生成的java代码。处理器AbstractProcessor 处理器的写法有固定的套路,继承AbstractProcessor。如下:java view plain copy 在CODE上查看代码片派生到我的代码片public class MyProcessor extends AbstractProcessor Override public synchronized void init(ProcessingEnvironment processingEnv) super.init(processingEnv); Override public Set getSupportedAnnotationTypes() return null; Override public SourceVersion getSupportedSourceVersion() return SourceVersion.latestSupported(); Override public boolean process(Set annotations, RoundEnvironment roundEnv) return true; init(ProcessingEnvironment processingEnv) 被注解处理工具调用,参数ProcessingEnvironment 提供了Element,Filer,Messager等工具getSupportedAnnotationTypes() 指定注解处理器是注册给那一个注解的,它是一个字符串的集合,意味着可以支持多个类型的注解,并且字符串是合法全名。getSupportedSourceVersion 指定Java版本process(Set annotations, RoundEnvironment roundEnv) 这个也是最主要的,在这里扫描和处理你的注解并生成Java代码,信息都在参数RoundEnvironment 里了,后面会介绍。在Java7 中还可以使用java view plain copy 在CODE上查看代码片派生到我的代码片SupportedSourceVersion(SourceVersion.latestSupported() SupportedAnnotationTypes( / 合法注解全名的集合 ) 代替 getSupportedSourceVersion() 和 getSupportedAnnotationType() ,没毛病,还可以在注解处理离器中使用注解。注册注解处理器打包注解处理器的时候需要一个特殊的文件 cessing.Processor 在 META-INF/services 路径下plain view plain copy 在CODE上查看代码片派生到我的代码片myprcessor.jar com example MyProcessor.class META-INF services cessing.Processor 打包进cessing.Processor的内容是处理器的合法全称,多个处理器之间换行。plain view plain copy 在CODE上查看代码片派生到我的代码片com.example.myprocess.MyProcessorA com.example.myprocess.MyProcessorB google提供了一个注册处理器的库plain view plain copy 在CODE上查看代码片派生到我的代码片compile com.google.auto.service:auto-service:1.0-rc2 一个注解搞定:java view plain copy 在CODE上查看代码片派生到我的代码片AutoService(Processor.class) public class MyProcessor extends AbstractProcessor . 读到这里ButterKnife用到的知识点我们都已经了解了1.自定义注解2.用注解处理器解析注解3.解析完成后生成Java文件BufferKnife使用:java view plain copy 在CODE上查看代码片派生到我的代码片public class MainActivity extends AppCompatActivity Bind(R.id.rxjava_demo) Button mRxJavaDemo; Override protected void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ButterKnife.bind(this); mRxJavaDemo.setText(Text); 然后我们编译一下,打开路径:/app/build/intermediates/classes/release/com/ming/rxdemo/MainActivity$ViewBinder.class这就是我们生成的Java文件,可以看到Button已经在bind里面初始化了。java view plain copy 在CODE上查看代码片派生到我的代码片public class MainActivity$ViewBinder implements ViewBinder public MainActivity$ViewBinder() public void bind(Finder finder, T target, Object source) View view = (View)finder.findRequiredView(source, 2131492944, field mRxJavaDemo); target.mRxJavaDemo = (Button)finder.castView(view, 2131492944, field mRxJavaDemo); public void unbind(T target) target.mRxJavaDemo = null; 接下来我们创建一个项目,写一个简单的用注解绑定控件的例子项目结构plain view plain copy 在CODE上查看代码片派生到我的代码片apt-demo bindview-annotation(Java Library) bindview-api(Android Library) bindview-compiler(Java Library) app(Android App) bindview-annotation 注解声明bindview-api 调用Android SDK APIbindview-compiler 注解处理器相关app 测试App1.在 bindview-annotation 下创建一个BindView注解,该注解返回一个值,整型,名字为value,用来表示控件ID。java view plain copy 在CODE上查看代码片派生到我的代码片Target(ElementType.FIELD) Retention(RetentionPolicy.CLASS) public interface BindView /* * 用来装id * * return */ int value(); 2.在 bindview-compiler 中创建注解处理器 BindViewProcessor 并注册,做基本的初始化工作。java view plain copy 在CODE上查看代码片派生到我的代码片AutoService(Processor.class) public class BindViewProcessor extends AbstractProcessor /* * 文件相关的辅助类 */ private Filer mFiler; /* * 元素相关的辅助类 */ private Elements mElementUtils; /* * 日志相关的辅助类 */ private Messager mMessager; /* * 解析的目标注解集合 */ private Map mAnnotatedClassMap = new HashMap(); Override public synchronized void init(ProcessingEnvironment processingEnv) super.init(processingEnv); mElementUtils = processingEnv.getElementUtils(); mMessager = processingEnv.getMessager(); mFiler = processingEnv.getFiler(); Override public Set getSupportedAnnotationTypes() Set pes = new LinkedHashSet(); types.add(BindView.class.getCanonicalName();/返回该注解处理器支持的注解集合 return types; Override public SourceVersion getSupportedSourceVersion() return SourceVersion.latestSupported(); Override public boolean process(Set annotations, RoundEnvironment roundEnv) return true; 是不是注意到了里面有个Map容器,而且类型是AnnotatedClass,这是干啥的呢?这个很好理解,我们在解析XML,解析Json的时候数据解析完之后是不是要以对象的形式表示出来,这里也一样,BindView用来标记类成员,一个类下可以有多个成员,好比一个Activity中可以有多个控件,一个容器下有多个控件等。如下:java view plain copy 在CODE上查看代码片派生到我的代码片package com.mingwei.myprocess.model; import com.mingwei.myprocess.TypeUtil; import com.squareup.javapoet.ClassName; import com.squareup.javapoet.JavaFile; import com.squareup.javapoet.MethodSpec; import com.squareup.javapoet.ParameterizedTypeName; import com.squareup.javapoet.TypeName; import com.squareup.javapoet.TypeSpec; import java.util.ArrayList; import java.util.List; import javax.lang.model.element.Modifier; import javax.lang.model.element.TypeElement; import javax.lang.model.util.Elements; /* * Created by mingwei on 12/10/16. * CSDN: /u013045971 * Github: /gumingwei */ public class AnnotatedClass /* * 类名 */ public TypeElement mClassElement; /* * 成员变量集合 */ public List mFiled; /* * 元素辅助类 */ public Elements mElementUtils; public AnnotatedClass(TypeElement classElement, Elements elementUtils) this.mClassElement = classElement; this.mElementUtils = elementUtils; this.mFiled = new ArrayList(); /* * 获取当前这个类的全名 */ public String getFullClassName() return mClassElement.getQualifiedName().toString(); /* * 添加一个成员 */ public void addField(BindViewField field) mFiled.add(field); /* * 输出Java */ public JavaFile generateFinder() return null; /* * 包名 */ public String getPackageName(TypeElement type) return mElementUtils.getPackageOf(type).getQualifiedName().toString(); /* * 类名 */ private static String getClassName(TypeElement type, String packageName) int packageLen = packageName.length() + 1; return type.getQualifiedName().toString().substring(packageLen).replace(., $); 成员用BindViewField表示,没什么复杂的逻辑,在构造函数判断类型和初始化,简单的get函数java view plain copy 在CODE上查看代码片派生到我的代码片package com.mingwei.myprocess.model; import com.mingwe.myanno.BindView; import javax.lang.model.element.Element; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Name; import javax.lang.model.element.VariableElement; import javax.lang.model.type.TypeMirror; /* * Created by mingwei on 12/10/16. * CSDN: /u013045971 * Github: /gumingwei * 被BindView注解标记的字段的模型类 */ public class BindViewField private VariableElement mFieldElement; private int mResId; public BindViewField(Element element) throws IllegalArgumentException if (element.getKind() != ElementKind.FIELD) /判断是否是类成员 throw new IllegalArgumentException(String.format(Only field can be annotated with %s, BindView.class.getSimpleName(); mFieldElement = (VariableElement) element; /获取注解和值 BindView bindView = mFieldElement.getAnnotation(BindView.class); mResId = bindView.value(); if (mResId 0) throw new IllegalArgumentException(String.format(value() in %s for field % is not valid, BindView.class.getSimpleName(), mFieldElement.getSimpleName(); public Name getFieldName() return mFieldElement.getSimpleName(); public int getResId() return mResId; public TypeMirror getFieldType() return mFieldElement.asType(); 这里看到了很多的Element,在Xml解析时候就有Element这个概念。在Java源文件中同样有Element概念:java view plain copy 在CODE上查看代码片派生到我的代码片package com.example; / PackageElement public class MyClass / TypeElement private int a; / VariableElement private Foo other; / VariableElement public Foo () / ExecuteableElement public void setA ( / ExecuteableElement int newA / TypeElement ) 接下来就是在处理器的process中解析注解了每次解析前都要清空,因为process方法可能不止走一次。拿到注解模型之后遍历调用生成Java代码java view plain copy 在CODE上查看代码片派生到我的代码片Override public boolean process(Set annotations, RoundEnvironment roundEnv) mAnnotatedClassMap.clear(); try processBindView(roundEnv); catch (IllegalArgumentException e) error(e.getMessage(); return true; try for (AnnotatedClass annotatedClass : mAnnotatedClassMap.values() info(generating file for %s, annotatedClass.getFullClassName(); annotatedClass.generateFinder().writeTo(mFiler); catch (Exception e) e.printStackTrace(); error(Generate file failed,reason:%s, e.getMessage(); return true; processBindView 和 getAnnotatedClassjava view plain copy 在CODE上查看代码片派生到我的代码片/* * 遍历目标RoundEnviroment * param roundEnv */ private void processBindView(RoundEnvironment roundEnv) for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class) AnnotatedClass annotatedClass = getAnnotatedClass(element); BindViewField field = new BindViewField(element); annotatedClass.addField(field); /* * 如果在map中存在就直接用,不存在就new出来放在map里 * param element */ private AnnotatedClass getAnnotatedClass(Element element) TypeElement encloseElement = (TypeElement) element.getEnclosingElement(); String fullClassName = encloseElement.getQualifiedName().toString(); AnnotatedClass annotatedClass = mAnnotatedClassMap.get(fullClassName); if (annotatedClass = ll) annotatedClass = new AnnotatedClass(encloseElement, mElementUtils); mAnnotatedClassMap.put(fullClassName, annotatedClass); return annotatedClass; 3.在生成Java之前 我们要在bindview-api 中创建一些类,配合 bindview-compiler 一起使用。你在使用Butterknife的时候不是要在onCreate里掉用一下BindView.bind(this)吗,那这个玩意是干什么呢。试想一下,前面做的一大堆工作是为了生成自动绑定控件的Java代码,如果生成的Java代码不能和你要使用的地方关联起来,那也是没有用的,可以把BindView.bind(this)理解为调用了你生成的Java代码,而生成了代码中完成了一些控件的初始化工作,自然你的控件就变得可用了。接口:Finder 定义findView方法实现类:ActivityFinder Activity中使用,ViewFinder View中使用接口:Injector inject方法将来是要创建在生成的Java文件中,用该方法中传递过来的参数进行控件的初始化。辅助类:ViewInjector 调用和传递参数这个代码我就不贴了,就一点点内容,一看就明白了。4.在AnnotatedClass中生成Java代码生成代码使用了一个很好用的库 Javapoet 。类,方法,都可以使用构建器构建出来,很好上手,再也不用拼接字符串了。哈哈哈哈java view plain copy 在CODE上查看代码片派生到我的代码片public JavaFile generateFinder() /构建方法 MethodSpec.Builder injectMethodBuilder = MethodSpec.methodBuilder(inject) .addModifiers(Modifier.PUBLIC)/添加描述 .addAnnotation(Override.class)/添加注解 .addParameter(TypeName.get(mClassElement.asType(), host, Modifier.FINAL)/添加参数 .addParameter(TypeName.OBJECT, source)/添加参数 .addParameter(TypeUtil.FINDER, finder);/添加参数 for (BindViewField field : mFiled) /添加一行 injectMethodBuilder.addStatement(host.$N=($T)finder.fi
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年宿州市中医医院招聘卫生专业技术人员36人模拟试卷及答案详解(典优)
- 呼啸山庄读书心得体会7篇
- 2025年城市污水处理厂智能化升级改造项目实施方案与效果评估报告
- 2025年新零售技术无人超市市场前景与挑战分析报告
- 2025年新能源汽车产业链上下游协同制造技术布局报告
- 2025年工业厂房装配式建筑设计与施工协调报告
- 2025年甘肃酒泉玉门市招聘村级后备干部模拟试卷及答案详解1套
- 2025年甘肃省嘉峪关开放大学招聘公益性岗位人员模拟试卷(含答案详解)
- 宾客关系主任(GRO)教学设计中职专业课-前厅服务与管理-旅游类-旅游大类
- 2025年甘肃省白银有色集团股份有限公司技能操作人员社会招聘552人笔试历年参考题库附带答案详解
- GB/T 21073-2007环氧涂层七丝预应力钢绞线
- GB/T 17980.37-2000农药田间药效试验准则(一)杀线虫剂防治胞囊线虫病
- 压力管道特性表
- 高级会计师评审个人业绩报告(精选9篇)
- 血管活性药物(ICU)课件
- “手电筒”模型-高考数学解题方法
- 储能型虚拟电厂的建设与思考分析报告
- 楼地面装饰构造(史上最全面)
- 海关AEO管理体系高级认证企业名录
- TTAF 068-2020 移动智能终端及应用软件用户个人信息保护实施指南 第8部分:隐私政策
- DB32∕T 4065-2021 建筑幕墙工程技术标准
评论
0/150
提交评论