Android自定义processor实现bindView功能的实例
作者:帅气的码农 发布时间:2023-09-07 17:16:37
一、简介
在现阶段的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
{
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment)
{
super.init(processingEnvironment);
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment)
{
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes()
{
return super.getSupportedAnnotationTypes();
}
@Override
public 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;
@Override
public Set<String> getSupportedAnnotationTypes() {
// 规定需要处理的注解
return Collections.singleton(LActivity.class.getCanonicalName());
}
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.out.println("DIProcessor");
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(LActivity.class);
for (Element element : elements) {
// 判断是否Class
TypeElement 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();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
elementUtils = processingEnv.getElementUtils();
}
@Override
public 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;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_processor);
DITestProcessorActivity.bindView(this);
buttonView.setOnClickListener(new View.OnClickListener() {
@Override
public 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);
}
}
<span style="font-size: 14px;">代码已经自动生成好了,我们就不需要再写findViewById()了:</span>
@LView(R.id.et_input)
EditText inputView;
@LView(R.id.button)
Button buttonView;
三、需要了解
我们上面例子主要运用了javapoet和auto-service,具体详细使用可以参考源码https://github.com/square/javapoet, 而AutoService比较简单,就是在使用Java APT的时候,使用AutoService注解,可以自动生成meta信息。网上有很多相关文章,可以好好整理学习下。
来源:http://www.cnblogs.com/limingblogs/p/8065051.html


猜你喜欢
- 拼图小游戏,学习阶段。很多不足,改进了一下演示图片:J_Puzzle.javaimport java.awt.BorderLayout;im
- kafka消费不到数据的排查集群上新安装并启动了3个kafka Broker,代码打包上传至集群,运行后发现一直消费不到数据,本地
- 1.1 简介 1.1.1 概述 Feign 旨在使编写 Java Http 客户端变得更容易。在使用 Ribbon + Rest
- 本文实例为大家分享了Android保存QQ密码功能的具体代码,供大家参考,具体内容如下技术要点:使用文件储存的方式保存数据实现步骤:①用户交
- 需求:将 一个容器List<Bean> 按照一定的字段进行分组,分组过后的值为特定的BEAN 里面的属性例如:假定有这样一个Be
- 一、在drawable下面添加xml文件rounded_editview.xml<?xml version="1.0&quo
- 1.选择一个WebService接口作测试假设 WebService url 为 http://ws.webxml.com.cn/WebSe
- 说起EventTrigger事件触发器,它的使用可以说是无处不在,EventTrigger继承了很多的事件接口,这些接口对我们开发是十分有用
- 程序可分为三部分:头文件:包含结构声明和使用这些结构的函数的原型源代码文件:包含与结构有关的函数的代码源代码文件:包含调用与结构相关的函数代
- 开发前准备1、密钥工具在线工具地址:https://miniu.alipay.com/keytool/create无需下载,直接在线生成你的
- 健康检查是Spring Boot Actuator中重要端点之一,可以非常容易查看应用运行至状态。本文在前文的基础上介绍如何自定义健康检查。
- 为了防止用户或者测试MM疯狂的点击某个button,写个方法防止按钮连续点击。具体实例代码如下所示:public class B
- 一,下载Zookeeper,地址为http://archive.apache.org/dist/zookeeper/,找到你要下载的版本,我
- 一)Document介绍API来源:在JDK中javax.xml.*包下使用场景:1、需要知道XML文档所有结构2、需要把文档一些元素排序3
- Class.forName(xxx.xx.xx) 返回的是一个类一.首先你要明白在java里面任何class都要装载在虚拟机上才能运行。1.
- 一、指标监控引入jar包: <dependency> &nb
- 在注册表启动项里添加一项,路径:SOFTWARE\Microsoft\Windows\CurrentVersion\Run或者直接:运行-&
- 在android 6.0开始,部分的权限需要我们动态申请,也就是说当我们的打开app的时候系统不会主动像您申请app所需要的部分权限,需要客
- Unsafe类是啥?Java最初被设计为一种安全的受控环境。尽管如此,Java HotSpot还是包含了一个“后门”,提供了一些可以直接操控
- 1.SQL注入:程序向后台数据库传递SQL时,用户提交的数据直接拼接到SQL语句中并执行,从而导入SQL注入攻击。字符型注入:黑色部分为拼接