微信公众号:Android研究院
关注可了解更多的Android知识,专注于移动领域。问题或建议,请公众号留言;
如果你觉得文章对你有帮助,欢迎赞赏[^1]
[TOC]
APT 详解
apt为何如此重要呢?现今越来越多的第三方库使用了apt技术,Dagger2、ButterKnife、ARouter等,在编译时根据annotation生成相关的代码逻辑,动态的生成Java class文件给开发带来了很大的便利。
首先要懂得annotation (注解)相关的基础知识。
APT 的全称为:Annotation Processing Tool 可以解释为注解处理器,
它对源代码文件进行检测找出其中的Annotation,使用指定的Annotation进行额外的处理。
Annotation处理器在处理Annotation时可以根据源文件中的Annotation生成额外的源文件和其它的文件(文件具体内容由Annotation处理器的编写者决定),APT还会编译生成的源文件和原来的源文件,将它们一起生成class文件。
下面我们通过一个例子来讲解apt实现的原理。本例实现gradle版本是3.0.1
- 新建一个名为annotationlib的java library,注意是Java library,不能是Android的library
@Target(ElementType.TYPE) // 注解作用在类上
@Retention(RetentionPolicy.CLASS)
public @interface Test {
String path();
}
build.gradle 配置
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
- 创建一个Java library 名为 TestCompiler
注意gradle的配置
apply plugin: 'java-library'
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':annotationlib')
implementation 'com.google.auto.service:auto-servic:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
com.google.auto.service:auto-service:1.0-rc4 是注册Processor的
com.squareup:javapoet:1.11.1 是生成class文件的第三方库
定义Processor类
@AutoService(Processor.class)
//指定编译的Java版本
@SupportedSourceVersion(SourceVersion.RELEASE_7)
//指定处理的注解
@SupportedAnnotationTypes({"demo.prim.com.annotationlib.Test"})
public class MyClass extends AbstractProcessor {
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
// 创建方法
MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC,Modifier.STATIC)
.addParameter(String[].class,"args")
.addStatement("$T.out.println($S)",System.class,"Hello JavaPoet")
.build();
//创建类
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWord")
.addModifiers(Modifier.PUBLIC,Modifier.FINAL)
.addMethod(main)
.build();
//创建Java文件
JavaFile file = JavaFile.builder("com.ecample.test", typeSpec)
.build();
try {
file.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
}
上述代码会生成如下的类
public final class HelloWord {
public static void main(String[] args) {
System.out.println("Hello JavaPoet");
}
}
- 在app中使用
这里需要注意如果gradle版本低于3.0.0 则需要如下配置
配置项目根目录的build.gradle
dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}
配置app的build.gradle
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt'
//...
dependencies {
//..
compile project(':annotation')
apt project(':compiler')
}
编译使用
在随意一个类添加@Test注解
@Test(path = "main")
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
点击Android Studio的Rebuild Project 会生成如下文件:
这时便可以调用HelloWord类了。
APT 实战
通过apt来简化 textView = findViewById(R.id.textView);。
一般的可以这样写:
public class ManagerFindByMainActivity {
public void findById(MainActivity activity) {
activity.textView = activity.findViewById(R.id.textView);
}
}
上面一段代码,这样写和之前的没有区别,反而每有一个Activity都要新建一个类去找到ID。那么我们是否可以通过apt来动态生成这个类呢?
答案当然是可以的。
- 在annotation lib 中创建两个注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface DIActivity {
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BYView {
int value() default 0;
}
- 在compiler lib 中创建processor
gradle 配置:
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.google.auto.service:auto-service:1.0-rc4'
implementation 'com.squareup:javapoet:1.11.1'
implementation project(':lib-annotation')
}
sourceCompatibility = "1.7"
targetCompatibility = "1.7"
核心代码实现:
@AutoService(Processor.class)
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({Constance.DIACTIVITY})
public class ByProcessor extends AbstractProcessor {
private Elements elementUtils;
private Types typeUtils;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
}
@Override
public boolean process(Set extends TypeElement> set, RoundEnvironment roundEnvironment) {
if (set != null) {
Set extends Element> elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(DIActivity.class);
if (elementsAnnotatedWith != null) {//获取设置DIActivity 注解的节点
//判断注解的节点是否为Activity
TypeElement typeElement = elementUtils.getTypeElement(Constance.Activity);
for (Element element : elementsAnnotatedWith) {
TypeMirror typeMirror = element.asType();//获取注解节点的类的信息
DIActivity annotation = element.getAnnotation(DIActivity.class);//获取注解的信息
if (typeUtils.isSubtype(typeMirror, typeElement.asType())) {//注解在Activity的类上面
TypeElement classElement = (TypeElement) element;//获取节点的具体类型
//创建参数 Map>> routes
ParameterSpec altlas = ParameterSpec
.builder(ClassName.get(typeMirror), "activity")//参数名
.build();
//创建方法
MethodSpec.Builder method = MethodSpec.methodBuilder
("findById")
// .addAnnotation(Override.class)
.addModifiers(PUBLIC, STATIC)
.returns(TypeName.VOID)
.addParameter(altlas);
//创建函数体
//获取TypeElement的所有成员变量和成员方法
List extends Element> allMembers = elementUtils.getAllMembers(classElement);//??
//遍历成员变量
for (Element member : allMembers) {
//找到被BYView注解的成员变量
BYView byView = member.getAnnotation(BYView.class);
if (byView == null) {
continue;
}
//构建函数体
method.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)",
member.getSimpleName(),//注解节点变量的名称
ClassName.get(member.asType()).toString(),//注解节点变量的类型
byView.value()));//注解的值
}
//创建类
TypeSpec typeSpec = TypeSpec.classBuilder("ManagerFindBy" + element.getSimpleName())
.addModifiers(PUBLIC, FINAL)//作用域
.addMethod(method.build())//添加方法
.build();
//创建Javaclass 文件
JavaFile javaFile = JavaFile.builder("com.prim.find.by", typeSpec).build();
try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
}
} else {
throw new IllegalArgumentException("@DIActivity must of Activity");
}
}
}
return true;
}
return false;
}
}
- Activity 中使用注解
implementation project(':lib_annotation')
annotationProcessor project(':lib_compiler')
@DIActivity
public class MainActivity extends AppCompatActivity {
@BYView(R.id.textView)
public TextView textView;
@BYView(R.id.textView1)
public TextView textView1;
@BYView(R.id.textView2)
public TextView textView2;
@BYView(R.id.button)
public Button button;
}
@DIActivity
public class SencodActivity extends AppCompatActivity {
@BYView(R.id.sencodText)
public TextView sencodText;
}
然后RebuildProject,就会得到:
- 使用生成的类
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ManagerFindByMainActivity.findById(this);
textView.setText("我是APT找到的");
textView1.setText("我是APT找到的 --> 1");
textView2.setText("我是APT找到的 --> 2");
button.setText("我被APT找到我要跳转");
}
public void click(View view) {
Intent intent = new Intent(this, SencodActivity.class);
startActivity(intent);
}
项目地址
常用方法
常用Element子类
TypeElement:类
ExecutableElement:成员方法
VariableElement:成员变量
通过包名和类名获取TypeName
TypeName targetClassName = ClassName.get(“PackageName”, “ClassName”);通过Element获取TypeName
TypeName type = TypeName.get(element.asType());获取TypeElement的包名
String packageName = processingEnv.getElementUtils().getPackageOf(type).getQualifiedName().toString();获取TypeElement的所有成员变量和成员方法
List extends Element> members = processingEnv.getElementUtils().getAllMembers(typeElement);
参考文章
Android的组件化专题:
组件化配置
APT实战
路由框架原理
模块间的业务通信
赞赏
如果你觉得到Android研究院对你有帮助,欢迎赞赏,有你的支持,Android研究院一定会越来越好!