一、简介

在现阶段的Android开发中,注解越来越流行起来,比如ButterKnife,Retrofit,Dragger,EventBus等等都选择使用注解来配置。按照处理时期,注解又分为两种类型,一种是运行时注解,另一种是编译时注解,运行时注解由于性能问题被一些人所诟病。编译时注解的核心依赖APT(Annotation Processing Tools)实现,原理是在某些代码元素上(如类型、函数、字段等)添加注解,在编译时编译器会检查AbstractProcessor的子类,并且调用该类型的process函数,然后将添加了注解的所有元素都传递到process函数中,使得开发人员可以在编译器进行相应的处理,例如,根据注解生成新的Java类,这也就是EventBus,Retrofit,Dragger等开源库的基本原理。 
Java API已经提供了扫描源码并解析注解的框架,你可以继承AbstractProcessor类来提供实现自己的解析注解逻辑。下边我们将学习如何在Android Studio中通过编译时注解生成java文件。

二、概念

注解处理器是一个在javac中的,用来编译时扫描和处理的注解的工具。你可以为特定的注解,注册你自己的注解处理器。
注解处理器可以生成Java代码,这些生成的Java代码会组成 .java 文件,但不能修改已经存在的Java类(即不能向已有的类中添加方法)。而这些生成的Java文件,会同时与其他普通的手写Java源代码一起被javac编译。

AbstractProcessor位于javax.annotation.processing包下,我们自己写processor需要继承它:

public class LProcessor extends AbstractProcessor
{@Overridepublic synchronized void init(ProcessingEnvironment processingEnvironment){super.init(processingEnvironment);}@Overridepublic boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment){return false;}@Overridepublic Set<String> getSupportedAnnotationTypes(){return super.getSupportedAnnotationTypes();}@Overridepublic SourceVersion getSupportedSourceVersion(){return super.getSupportedSourceVersion();}
}

  

对上面代码方法简单讲解

  • init(ProcessingEnvironment processingEnvironment): 每一个注解处理器类都必须有一个空的构造函数。然而,这里有一个特殊的init()方法,它会被注解处理工具调用,并输入ProcessingEnviroment参数。ProcessingEnviroment提供很多有用的工具类Elements,Types和Filer。后面我们将看到详细的内容。
  • process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment): 这相当于每个处理器的主函数main()。你在这里写你的扫描、评估和处理注解的代码,以及生成Java文件。输入参数RoundEnviroment,可以让你查询出包含特定注解的被注解元素。后面我们将看到详细的内容。
  • getSupportedAnnotationTypes(): 这里你必须指定,这个注解处理器是注册给哪个注解的。注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。换句话说,你在这里定义你的注解处理器注册到哪些注解上。
  • getSupportedSourceVersion(): 用来指定你使用的Java版本。通常这里返回SourceVersion.latestSupported()。然而,如果你有足够的理由只支持Java 7的话,你也可以返回SourceVersion.RELEASE_7。注意:在Java 7以后,你也可以使用注解来代替getSupportedAnnotationTypes()和getSupportedSourceVersion()。

我们先创建一个java module LProcessor

@AutoService(Processor.class)
public class LProcessor extends AbstractProcessor {private Elements elementUtils;@Overridepublic Set<String> getSupportedAnnotationTypes() {// 规定需要处理的注解return Collections.singleton(LActivity.class.getCanonicalName());}@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {System.out.println("DIProcessor");Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(LActivity.class);for (Element element : elements) {// 判断是否ClassTypeElement typeElement = (TypeElement) element;List<? extends Element> members = elementUtils.getAllMembers(typeElement);MethodSpec.Builder bindViewMethodSpecBuilder = MethodSpec.methodBuilder("bindView").addModifiers(Modifier.PUBLIC, Modifier.STATIC).returns(TypeName.VOID).addParameter(ClassName.get(typeElement.asType()), "activity");for (Element item : members) {LView diView = item.getAnnotation(LView.class);if (diView == null){continue;}bindViewMethodSpecBuilder.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)",item.getSimpleName(),ClassName.get(item.asType()).toString(),diView.value()));}TypeSpec typeSpec = TypeSpec.classBuilder("DI" + element.getSimpleName()).addModifiers(Modifier.PUBLIC, Modifier.FINAL).addMethod(bindViewMethodSpecBuilder.build()).build();JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build();try {javaFile.writeTo(processingEnv.getFiler());} catch (IOException e) {e.printStackTrace();}}return true;}private String getPackageName(TypeElement type) {return elementUtils.getPackageOf(type).getQualifiedName().toString();}@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);elementUtils = processingEnv.getElementUtils();}@Overridepublic SourceVersion getSupportedSourceVersion() {return SourceVersion.RELEASE_7;}
}

这里面我们引入了两个库
compile 'com.google.auto.service:auto-service:1.0-rc2'
compile 'com.squareup:javapoet:1.7.0'

 我们再创建一个java module anotation

可见,是两个注解类:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface LActivity {
}

  

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LView {int value() default 0;
}

之后我们主工程引入这两个module 就可以在我们主工程下面用这个注解了,我们make project之后会在工程目录下build/generated/source/apt下生成对应的java源文件,比如我在下面的activity类使用了定义的注解:

@LActivity
public class TestProcessorActivity extends Activity {@LView(R.id.et_input)EditText inputView;@LView(R.id.button)Button buttonView;@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_processor);DITestProcessorActivity.bindView(this);buttonView.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Toast.makeText(TestProcessorActivity.this  , inputView.getText().toString() , Toast.LENGTH_SHORT).show();}});}
}

  

则在build/generated/source/apt下生成DITestProcessorActivity.java

public final class DITestProcessorActivity {public static void bindView(TestProcessorActivity activity) {activity.inputView = (android.widget.EditText) activity.findViewById(2131165237);activity.buttonView = (android.widget.Button) activity.findViewById(2131165220);}
}

代码已经自动生成好了,我们就不需要再写findViewById()了,只需要调用DITestProcessorActivity的bindView方法就可以了。
@LView(R.id.et_input)
EditText inputView;
@LView(R.id.button)
Button buttonView;

  

三、需要了解

我们上面例子主要运用了javapoet和auto-service,具体详细使用可以参考源码https://github.com/square/javapoet, 而AutoService比较简单,https://github.com/google/auto/tree/master/service 就是在使用Java APT的时候,使用AutoService注解,可以自动生成meta信息。网上有很多相关文章,可以好好整理学习下。

转载于:https://www.cnblogs.com/limingblogs/p/8065051.html

Android自定义processor实现bindView功能相关推荐

  1. android xml画圆,Android自定义View画圆功能

    本文实例为大家分享了Android自定义View画圆的具体代码,供大家参考,具体内容如下 引入布局 xmlns:tools="http://schemas.android.com/tools ...

  2. Android 自定义ViewPager设置屏蔽左右滑动事件

    只要有欲望,就应该有奋斗的心.... 屏蔽左右滑动事件的viewPager public class CustomNoScrollViewPager extends ViewPager{private ...

  3. android代码实现手机加速功能,Android自定义View实现内存清理加速球效果

    Android自定义View实现内存清理加速球效果 发布时间:2020-09-21 22:21:57 来源:脚本之家 阅读:105 作者:程序员的自我反思 前言 用过猎豹清理大师或者相类似的安全软件, ...

  4. Android m 自定义下拉菜单,Android实现动画效果的自定义下拉菜单功能

    我们在购物APP里面设置收货地址时,都会有让我们选择省份及城市的下拉菜单项.今天我将使用Android原生的 Spinner 控件来实现一个自定义的下拉菜单功能,并配上一个透明渐变动画效果. 要实现的 ...

  5. android 实现自动拍照,Android自定义相机实现定时拍照功能

    这篇博客为大家介绍Android自定义相机,并且实现倒计时拍照功能. 首先自定义拍照会用到SurfaceView控件显示照片的预览区域,以下是布局文件: activity_main.xml andro ...

  6. Android仿支付宝UI功能开发,Android 自定义view仿支付宝咻一咻功能

    支付宝上有一个咻一咻的功能,就是点击图片后四周有水波纹的这种效果,今天也写一个类似的功能. 效果如下所示: 思路: 就是几个圆的半径不断在变大,这个可以使用动画缩放实现,还有透明动画 还有就是这是好几 ...

  7. android 自定义 对号,Android自定义View实现打钩动画功能

    先上效果图 动图 静态图 1. 回顾 [Android自定义View:一个精致的打钩小动画]上一篇文章,我们已经实现了基本上实现了控件的效果了,但是...但是...过了三四天后,仔细看回自己写的代码, ...

  8. android 微信朋友圈 全功能,Android仿微信朋友圈文字展开全文功能 Android自定义TextView仿微信朋友圈文字展开全文功能...

    Android自定义TextView仿微信朋友圈文字信息,展开全文功能 代码及注释如下: 首先写一个xml文件 showmore.xml: android:orientation="vert ...

  9. android 开发打赏布局,Android自定义View模仿虎扑直播界面的打赏按钮功能

    Android自定义View模仿虎扑直播界面的打赏按钮功能 发布时间:2020-09-28 12:15:53 来源:脚本之家 阅读:77 作者:shenhuniurou 前言 作为一个资深篮球爱好者, ...

最新文章

  1. C#实现汉字转化为拼音
  2. Rails字符集问题
  3. Linux中如何安装MySQL详细步骤
  4. 【PHP】curl_init() 如何排错????
  5. 华软oracle,ORACLE 表空间的简单理解
  6. 人工神经网络——笔记摘抄1
  7. 装饰一个类及内部方法
  8. 2.3单链表的基本使用及其cpp示例
  9. VS Code:高效前端程序员的秘密武器
  10. ROS报错:/usr/include/eigen3/Eigen/src/Core/util/StaticAssert.h:119:9: error: ‘YOU_MIXED_DIFFERENT
  11. IOS炫酷的引导界面
  12. 10 个基于 JavaScript 的机器学习实例
  13. 算法: 最大矩形面积85. Maximal Rectangle
  14. MATLAB--求一个矩阵中所有元素的平均值
  15. python找不同_用Python玩大家来找茬
  16. javascript总复习
  17. mysql netbeans_使用Netbeans操作MySQL数据库
  18. [编译原理读书笔记][第4章 语法分析]
  19. 【AC.HASH】OpenHarmony啃论文俱乐部——在基于位置的隐私感知服务中实现K-匿名之浅析
  20. 【知识兔】Excel教程:文本转数值的这些套路,你都会了吗?

热门文章

  1. [翻译]CryEngine3中光照的美术提示
  2. 【MyBatis笔记】02-MyBatis配置SQL打印
  3. [转]浅谈 python multiprocessing(多进程)下如何共享变量
  4. adobe blueprint
  5. 人脸识别sdk_开发实录:免费人脸识别SDK实现人证比对全过程
  6. centos桌面系统使用记录
  7. c#截取后几位_C#几种截取字符串的方法小结
  8. ie不加载jre_国内银行为兼容XP/IE6 竟然篡改IE安全协议把所有用户拖下水
  9. arm-linux启动,linux启动流程arm
  10. Python之 continue继续循环