android的APT技术
转载请标明出处:https:////www.cnblogs.com/tangZH/p/12343786.html,
APT 是Annotation Processing Tool 的简称。
事实上它是javac的一个工具,命令行运行javac后便可以看到:

接下来我们就来实现一个apt的实例,类似于ButterKnife中@BindView注解,基本步骤如下:
1、定义要被处理的注解。
2、定义注解处理器(生成具体的类)。
3、调用处理器生成的代码
对应的,我们在工程中需要有这几个模块:
1、app。测试我们的功能
2、apt-annotation。一个Java library module,放置我们自定义注解
3、apt-processor。一个Java library module,注解处理器模块
4、apt-sdk。一个Android library module,通过反射调用apt-processor模块生成的方法,实现view的绑定。
工程目录如下:

1、在apt-annotation中自定义注解:
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
2、apt-processor中引入依赖,它需要依赖apt-annotation,同时还需要依赖auto-service第三方库,后面创建注解处理器的时候需要用到。
apt-processor/build.gradle文件中:
implementation project(':apt-annotation')
implementation 'com.google.auto.service:auto-service:1.0-rc2'
3、在pat-processor中创建注解处理器:
处理器需要继承AbstractProcessor,注意该module是 java module,如果创建的是android module的话那么就会找不到AbstractProcessor
@AutoService(Processor.class)
@SuppressWarnings("unused")
public class BindViewProcessor extends AbstractProcessor {
private Elements mElementUtils;
private Map<String, ClassCreatorFactory> mClassCreatorFactoryMap = new HashMap<>(); @Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//拿到工具类
mElementUtils = processingEnvironment.getElementUtils();
} @Override
public Set<String> getSupportedAnnotationTypes() {
//这个注解处理器是给哪个注解用的
HashSet<String> supportType = new LinkedHashSet<>();
supportType.add(BindView.class.getCanonicalName());
return supportType;
} @Override
public SourceVersion getSupportedSourceVersion() {
//返回java版本
return SourceVersion.latestSupported();
} @Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
mClassCreatorFactoryMap.clear();
//得到所有包含该注解的element集合
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class); for (Element element : elements) {
//转换为VariableElement,VariableElement为element的子类
VariableElement variableElement = (VariableElement) element;
//可以获取类的信息的element,也是element的子类
TypeElement classElement = (TypeElement) variableElement.getEnclosingElement();
//获取包名加类名
String fullClassName = classElement.getQualifiedName().toString(); //保存到集合中
ClassCreatorFactory factory = mClassCreatorFactoryMap.get(fullClassName);
if (factory == null) {
factory = new ClassCreatorFactory(mElementUtils, classElement);
mClassCreatorFactoryMap.put(fullClassName, factory);
}
BindView bindViewAnnotation = variableElement.getAnnotation(BindView.class);
int id = bindViewAnnotation.value();
factory.putElement(id, variableElement);
}
//开始创建java类
for (String key : mClassCreatorFactoryMap.keySet()) {
ClassCreatorFactory factory = mClassCreatorFactoryMap.get(key);
try {
JavaFileObject fileObject = processingEnv.getFiler().createSourceFile(
factory.getClassFullName(), factory.getTypeElement());
Writer writer = fileObject.openWriter();
//写入java代码
writer.write(factory.generateJavaCode());
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return true;
}
}
需要注意的是代码中不能有中文,否则编译不通过,我这里为了方便注释解释加上了中文。
ClassCreatorFactory的代码如下,这个类负责提供需要写入新的类的代码:
public class ClassCreatorFactory {
private String mBindClassName;
private String mPackageName;
private TypeElement mTypeElement;
private Map<Integer, VariableElement> mVariableElementMap = new HashMap<>();
ClassCreatorFactory(Elements elementUtils, TypeElement classElement) {
this.mTypeElement = classElement;
//PackageElement是element的子类,可以拿到包信息
PackageElement packageElement = elementUtils.getPackageOf(mTypeElement);
String packageName = packageElement.getQualifiedName().toString();
String className = mTypeElement.getSimpleName().toString();
this.mPackageName = packageName;
//生成的类的名称
this.mBindClassName = className + "_ViewBinding";
}
public void putElement(int id, VariableElement element) {
mVariableElementMap.put(id, element);
}
public String generateJavaCode() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("/**\n" + " * Auto Created by apt\n" + "*/\n");
stringBuilder.append("package ").append(mPackageName).append(";\n");
stringBuilder.append('\n');
stringBuilder.append("public class ").append(mBindClassName);
stringBuilder.append(" {\n");
generateBindViewMethods(stringBuilder);
stringBuilder.append('\n');
stringBuilder.append("}\n");
return stringBuilder.toString();
}
private void generateBindViewMethods(StringBuilder stringBuilder) {
stringBuilder.append("\tpublic void bindView(");
stringBuilder.append(mTypeElement.getQualifiedName());
stringBuilder.append(" owner) {\n");
for (int id : mVariableElementMap.keySet()) {
VariableElement variableElement = mVariableElementMap.get(id);
String viewName = variableElement.getSimpleName().toString();
String viewType = variableElement.asType().toString();
stringBuilder.append("\t\towner.");
stringBuilder.append(viewName);
stringBuilder.append(" = ");
stringBuilder.append("(");
stringBuilder.append(viewType);
stringBuilder.append(")(((android.app.Activity)owner).findViewById( ");
stringBuilder.append(id);
stringBuilder.append("));\n");
}
stringBuilder.append(" }\n");
}
public String getClassFullName() {
return mPackageName + "." + mBindClassName;
}
public TypeElement getTypeElement() {
return mTypeElement;
}
}
先不谈apt-sdk模块,我们先来看看生成的代码是怎么样的。
在app的gradle中引入:
implementation project(':apt-annotation')
annotationProcessor project(':apt-processor')
特别要注意的是apt-processor模块的依赖引进要用 annotationProcessor,否则编译报错
两个activity中:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView textView;
@BindView(R.id.tv_1)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
public class Main2Activity extends AppCompatActivity {
@BindView(R.id.tv_2)
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main2);
}
}
rebuild一下便可以看到在这个目录下有我们生成的文件了。
gradle高版本出现编译后没出现文件的问题,无奈只好降低版本,我使用的版本是gradle 3.1.4 + gralde_wrap gradle-4.4-all.zip

点进入其中一个可以看到是这样的代码:
/**
* Auto Created by apt
*/
package com.example.aptsample; public class MainActivity_ViewBinding {
public void bindView(com.example.aptsample.MainActivity owner) {
owner.tv = (android.widget.TextView)(((android.app.Activity)owner).findViewById( 2131165360));
owner.textView = (android.widget.TextView)(((android.app.Activity)owner).findViewById( 2131165359));
} }
所以我们只要调用bindView就能够找到该view了,这也是apt-sdk要做的事情。
4、在apt-sdk中创建类,反射调用生成的类中的方法
public class DataApi {
public static void bindView(Activity activity) {
Class clazz = activity.getClass();
try {
Class<?> bindViewClass = Class.forName(clazz.getName() + "_ViewBinding");
Method method = bindViewClass.getMethod("bindView", activity.getClass());
method.invoke(bindViewClass.newInstance(),activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
5、app的gradle中引入apt-sdk,然后代码调用DataApi的方法
implementation project(':apt-annotation')
annotationProcessor project(':apt-processor')
implementation project(':apt-sdk')
app的MainActivity中实现
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv)
TextView textView;
@BindView(R.id.tv_1)
TextView tv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DataApi.bindView(this);
tv.setText("a");
}
}
这样就大功告成了
精美原文:http://77blogs.com/?p=199
源码地址:https://github.com/TZHANHONG/AptAutoBindView
android的APT技术的更多相关文章
- Android的NDK技术
Android的NDK技术
- 本人讲课时录制的Android应用开发技术教学视频
网盘地址:http://yun.baidu.com/pcloud/album/info?query_uk=1963923831&album_id=3523786484935252365 本人讲 ...
- 【转】Android 防破解技术简介
http://www.cnblogs.com/likeandroid/p/4888808.html Android 防破解技术简介 这几年随着互联网的不断发展,Android App 也越来越多!但是 ...
- Android推送技术研究
前言 最近研究Android推送的实现, 研究了两天一夜, 有了一点收获, 写下来既为了分享, 也为了吐槽. 需要说明的是有些东西偏底层硬件和通信行业, 我对这些一窍不通, 只能说说自己的理解. 为什 ...
- 全面了解Android热修复技术
WeTest 导读 本文探讨了Android热修复技术的发展脉络,现状及其未来. 热修复技术概述 热修复技术在近年来飞速发展,尤其是在InstantRun方案推出之后,各种热修复技术竞相涌现.国内大部 ...
- Android热修复技术原理详解(最新最全版本)
本文框架 什么是热修复? 热修复框架分类 技术原理及特点 Tinker框架解析 各框架对比图 总结 通过阅读本文,你会对热修复技术有更深的认知,本文会列出各类框架的优缺点以及技术原理,文章末尾简单 ...
- 聊聊真实的 Android TV 开发技术栈
智能电视越来越普及了,华为说四月发布智能电视跳票了,一加也说今后要布局智能电视,在智能电视方向,小米已经算是先驱了.但是还有不少开发把智能电视简单的理解成手机屏幕的放大,其实这两者并不一样. 一.序 ...
- Android 插件化技术窥探
在Android 插件化技术中(宿主app和插件app设置相同的sharedUserId),动态加载apk有两种方式: 一种是将资源主题包的apk安装到手机上再读取apk内的资源,这种方式的原理是将宿 ...
- Android 防破解技术简介
Android 防破解技术简介 这几年随着互联网的不断发展,Android App 也越来越多!但是随之而来的问题也越来越多,这其中比较令人头疼的问题就是:有些不法分子利用反编译技术破解 App,修改 ...
随机推荐
- [UVA1494] Qin Shi Huang's National Road System
题目 戳这里 题解 从今天起我要改邪归正,好好刷题准备联赛! 这是一道经典的最小生成树题目. 枚举每一条边作为道士要修的路,求出包含这条边的最小生成树. 先求出原图的最小生成树. 如果要删的边在最小生 ...
- [bzoj4942] [洛谷P3822] [NOI2017] 整数
题目链接 https://www.luogu.org/problemnew/show/P3822 想法 这个啊,就是线段树哇 最初的想法是每位一个节点,然后进位.退位找这一位前面第一个0或第一个1,然 ...
- 浅谈DFS,BFS,IDFS,A*等算法
搜索是编程的基础,是必须掌握的技能.--王主任 搜索分为盲目搜索和启发搜索 下面列举OI常用的盲目搜索: 1.dijkstra 2.SPFA 3.bfs 4.dfs 5.双向bfs 6.迭代加深搜索( ...
- python day01学习
1.python语言 # 89年 龟叔2.python的特点 # 优点 : 简明 简单 跨平台性好 # 缺点 : 慢 -执行速度相对其他语言慢 # 编程语言的分类: # 编译型语言: c c++ j ...
- python 爬虫过程中出现:ConnectionResetError: [WinError 10054] 远程主机强迫关闭了一个现有的连接
参考: https://blog.csdn.net/illegalname/article/details/77164521
- ps-如何移动照片里的内容
1.我们用内容感知移动工具把所要移动的区域大致勾选出来. 2.然后我们将所选区域拖动(点击鼠标左键不放拖动). 3.松开鼠标 4.仿制图章工具,alt 键取样,涂抹
- python 异常之进阶操作
1.文件分析 下面来做一些文件分析操作,分析整本书的信息. 知识点: string.split():将字符串分解为列表. open(filename,‘rb’)或者open(filename,enco ...
- java架构之路-(netty专题)netty的编解码(出入战)与粘包拆包
上次回归: 上次博客我们主要说了netty的基本使用,都是一些固定的模式去写的,我们只需要关注我们的拦截器怎么去写就可以了,然后我们用我们的基础示例,改造了一个简单的聊天室程序,可以看到内部加了一个S ...
- Go语言实现:【剑指offer】丑数
该题目来源于牛客网<剑指offer>专题. 把只包含质因子2.3和5的数称作丑数(Ugly Number).例如6.8都是丑数,但14不是,因为它包含质因子7.习惯上我们把1当做是第一个丑 ...
- SSL公钥证书传递进行隐匿传输数据
title: 使用X.509公钥证书传递进行隐匿传输数据 date: 2018-02-11 17:47:50 tags: --- 使用X.509公钥证书传递进行隐匿传输数据 看到国外一篇有关于在ssl ...