1 生成文件方式
编译期注解通过读取注解,然后处理注解字段Element,最终的目的是在编译代码之前生成所需源码文件作为工具类,减少手动书写代码。
生成文件的方式有多种,例如StringBuilder进行拼接,模板文件进行字段替换,javaPoet 生成。
模板生成代码
StringBuilder进行拼接,模板文件进行字段替换进行简单文件生成还好,如果是复杂文件,拼接起来会相当复杂如下所示:
private String brewCode(String className, String pkName, ArrayList mElementList) {
StringBuilder builder = new StringBuilder();
builder.append("package " + pkName + ";\n\n");
builder.append("public class " + className + "$ViewBinding implements com.ldx.injectlib.InjectIoc { \n\n");
builder.append("@Override\n\n");
builder.append("public void inject("+"Object"+" obj1"+"){ \n\n");
builder.append(" "+pkName+"."+className+" obj" +" = "+"("+pkName+"."+className+")"+"obj1;\n\n");
for (VariableElement element : mElementList){
//3.获取注解的成员变量名
String bindViewFiledName = element.getSimpleName().toString();
//变量类型
String bindViewFiledClassType = element.asType().toString();
BindView bindAnnotation = element.getAnnotation(BindView.class);
int id = bindAnnotation.value();
String info = String.format("%s %s = findViewById(%d)", bindViewFiledClassType, bindViewFiledName, id);
builder.append(" System.out.println(\"" + info + "\");\n\n");
builder.append(" obj."+bindViewFiledName+" = "+"obj.findViewById("+id+");"+"\n\n");
}
builder.append("}\n\n");
builder.append("}");
return builder.toString();
}
javaPoet生成代码是Square出品的sdk,学习成本较小,用起来相当顺手。
简单代码示例
private void createClass(){
//https://github.com/square/javapoet
try {
MethodSpec methodSpec = MethodSpec.methodBuilder("main")
.addModifiers( javax.lang.model.element.Modifier.PUBLIC, javax.lang.model.element.Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class,"args")
.addStatement("$T.out.println($S)",System.class,"hello world!")
.build();
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers( javax.lang.model.element.Modifier.PUBLIC, javax.lang.model.element.Modifier.FINAL)
.addMethod(methodSpec)
.build();
JavaFile javaFile = JavaFile.builder("com.ldx.canvasdrawdemo", typeSpec)
.build();
mMessager.printMessage(Diagnostic.Kind.NOTE, javaFile.toString()+"");
javaFile.writeTo(mFilerUtils);
} catch (IOException e) {
e.printStackTrace();
}
}
最终生成的文件:
package com.ldx.canvasdrawdemo;
import java.lang.String;
import java.lang.System;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("hello world!");
}
}
2 javapoet 使用讲解
需要引入javapoet的库 implementation 'com.squareup:javapoet:1.11.1';
本篇大致讲解,详细内容参照(https://github.com/square/javapoet),一般生成代码的方式为从Class开始,缺少什么在前面生成对应的代码模块,add到classtype中最终完成class的生成。
常用到的api:
用到的类(多为建造者模式,链式调用):
- TypeSpec:用于生成类、接口、枚举对象的类(class interface enum)
- MethodSpec:用于生成方法对象的类(生成method)
- ParameterSpec:用于生成参数对象的类(方法的参数)
- AnnotationSpec:用于生成注解对象的类(生成注解)
- FieldSpec:用于配置生成成员变量的类(成员变量)
- ClassName:通过包名和类名生成的对象,指明为某个Class,功能类似xxx.class
- ParameterizedTypeName:通过MainClass和IncludeClass生成包含泛型的Class
用到的方法:
创建类:
TypeSpec.classBuilder("类名“)
TypeSpec.classBuilder(ClassName className)
创建接口:
TypeSpec.interfaceBuilder("接口名称")
TypeSpec.interfaceBuilder(ClassName className)
创建枚举:
TypeSpec.enumBuilder("枚举名称")
TypeSpec.enumBuilder(ClassName className)
添加修饰符,public,static,final,private ,protected
addModifiers(Modifier... modifiers)
举例:
private void test2(){
TypeSpec myInterface = TypeSpec.interfaceBuilder("MyInterface")
.addModifiers(Modifier.PUBLIC)
.addField(FieldSpec.builder(String.class, "name")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC, Modifier.FINAL)
.initializer("$S", "liming")
.build())
.addMethod(MethodSpec.methodBuilder("getName")
.addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT)
.build())
.build();
JavaFile file1 = JavaFile.builder("com.ldx.canvasdrawdemo", myInterface).build();
mMessager.printMessage(Diagnostic.Kind.NOTE, file1.toString()+"");
TypeSpec myEnum = TypeSpec.enumBuilder("Range")
.addModifiers(Modifier.PUBLIC)
.addEnumConstant("ONE")
.addEnumConstant("TWO")
.addEnumConstant("THREE")
.build();
JavaFile file2 = JavaFile.builder("com.ldx.canvasdrawdemo", myEnum).build();
mMessager.printMessage(Diagnostic.Kind.NOTE, file2.toString()+"");
MethodSpec methodSpec = MethodSpec.methodBuilder("main")
.addModifiers( javax.lang.model.element.Modifier.PUBLIC, javax.lang.model.element.Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class,"args")
.addStatement("$T.out.println($S)",System.class,"hello world!")
.build();
TypeSpec typeSpec = TypeSpec.classBuilder("HelloWorld")
.addModifiers( javax.lang.model.element.Modifier.PUBLIC, javax.lang.model.element.Modifier.FINAL)
.addMethod(methodSpec)
.build();
JavaFile javaFile3 = JavaFile.builder("com.ldx.canvasdrawdemo", typeSpec)
.build();
mMessager.printMessage(Diagnostic.Kind.NOTE, javaFile3.toString()+"");
}
生成的文件:
package com.ldx.canvasdrawdemo;
import java.lang.String;
public interface MyInterface {
String name = "liming";
void getName();
}
package com.ldx.canvasdrawdemo;
public enum Range {
ONE,
TWO,
THREE
}
package com.ldx.canvasdrawdemo;
import java.lang.String;
import java.lang.System;
public final class HelloWorld {
public static void main(String[] args) {
System.out.println("hello world!");
}
}
继承类:
.superclass(ClassName className)
实现接口
.addSuperinterface(ClassName className)
当继承父类存在泛型时,需要使用ParameterizedTypeName
ParameterizedTypeName get(ClassName rawType, TypeName... typeArguments)
ClassName superClass = ClassName.get("android.support.v7.app","AppCompatActivity");
ClassName onClickClass = ClassName.get("android.view", "View.OnClickListener");
TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder("TwoActivity")
.addModifiers(Modifier.PUBLIC)
.superclass(superClass)
.addSuperinterface(onClickClass);
TypeSpec mainActivity = typeSpecBuilder
.build();
JavaFile file = JavaFile.builder("com.ldx.canvasdrawdemo", mainActivity).build();
mMessager.printMessage(Diagnostic.Kind.NOTE, file.toString()+"");
生成代码
package com.ldx.canvasdrawdemo;
import android.support.v7.app.AppCompatActivity;
import android.view.View.OnClickListener;
public class TwoActivity extends AppCompatActivity implements View.OnClickListener {
}
创建函数
MethodSpec.methodBuilder(String name)
创建构造方法
MethodSpec.constructorBuilder()
添加方法
addMethod(MethodSpec methodSpec)
为方法添加参数
addParameter(ParameterSpec parameterSpec)
设置返回值
returns(TypeName returnType)
举例:
ClassName strclass = ClassName.get("java.lang", "String");
TypeSpec myclass = TypeSpec.classBuilder("MyClass")
.addModifiers(Modifier.PUBLIC)
.addMethod(MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(strclass,"myStr")
.build())
.addMethod(MethodSpec.methodBuilder("myMethod")
.addModifiers(Modifier.PRIVATE)
.returns(strclass)
.addStatement("return $S","name")
.build())
.build();
JavaFile file = JavaFile.builder("com.ldx.canvasdrawdemo", myclass).build();
mMessager.printMessage(Diagnostic.Kind.NOTE, file.toString()+"");
生成的代码
package com.ldx.canvasdrawdemo;
import java.lang.String;
public class MyClass {
public MyClass(String myStr) {
}
private String myMethod() {
return "name";
}
}
为方法体添加代码
addCode()
addCode(String format, Object... args)
addStatement()
addStatement(String format, Object... args)
addCode 和addStatement的区别是addCode会把添加的String原封不动的作为代码,addState则会帮助添加;等操作。
JavaPoet中,format中存在三种特定的占位符:
$T 在JavaPoet代指的是TypeName,该模板主要将Class抽象出来,用传入的TypeName指向的Class来代替。
$N在JavaPoet中代指的是一个名称,例如调用的方法名称,变量名称,这一类存在意思的名称
$S & $L
$S在JavaPoet中会利用指定字符替换$S的地方,替换后的内容,$S自带了双引号,$L不自带双引号
抛出异常
.addException(TypeName name)
MethodSpec onClick = MethodSpec.methodBuilder("onClick")
.addAnnotation(override)
.addModifiers(Modifier.PUBLIC)
.addParameter(viewParameter)
.addJavadoc("自动生成代码,勿删")
.addCode("String name = \"xiaoming\";\n\n")
.addStatement("$T good = \"\"",String.class)
.addStatement("$T twoView = null",viewClass)
.addStatement("good = name.$N()","toString")
.addStatement("twoView = new $T(this)",viewClass)
.addStatement("$T lll = $S",String.class,"lllll")
.addStatement("$T tttt = $L",int.class,22)
.build();
生成注解
AnnotationSpec.builder(override).build()
ClassName override = ClassName.get("java.lang", "Override");
ClassName bundle = ClassName.get("android.os", "Bundle");
ClassName nullable = ClassName.get("android.support.annotation", "Nullable");
AnnotationSpec overRideAnno = AnnotationSpec.builder(override).build();
AnnotationSpec nullableAnno = AnnotationSpec.builder(nullable).build();
标识class
ClassName superClass = ClassName.get("android.support.v7.app","AppCompatActivity");
ClassName onClickClass = ClassName.get("android.view", "View.OnClickListener");
ClassName viewClass = ClassName.get("android.view", "View");
生成成员变量
Class stringClazz = String.class;
FieldSpec fieldSpec = FieldSpec.builder(stringClazz, "mName", Modifier.PRIVATE).build();
FieldSpec fieldSpec2 = FieldSpec.builder(stringClazz, "mStr2")
.addModifiers(Modifier.PUBLIC)
.build();
设置注解
addAnnotation(AnnotationSpec annotationSpec)
addAnnotation(ClassName annotation)
addAnnotation(Class> annotation)
ClassName override = ClassName.get("java.lang", "Override");
ClassName bundle = ClassName.get("android.os", "Bundle");
ClassName nullable = ClassName.get("android.support.annotation", "Nullable");
AnnotationSpec overRideAnno = AnnotationSpec.builder(override).build();
AnnotationSpec nullableAnno = AnnotationSpec.builder(nullable).build();
ParameterSpec savedInstanceState = ParameterSpec.builder(bundle, "savedInstanceState")
.addAnnotation(nullable)
// .addAnnotation(nullableAnno)//这种方式添加也可以
.build();
ParameterSpec viewParameter = ParameterSpec.builder(viewClass, "view")
.build();
MethodSpec onCreate = MethodSpec.methodBuilder("onCreate")
.addAnnotation(override)
// .addAnnotation(overRideAnno)//这种方式添加也可以
.addModifiers(Modifier.PROTECTED)
.addParameter(savedInstanceState)
.addStatement("super.onCreate(savedInstanceState)")
.addStatement("setContentView(R.layout.activity_main)")
.build();
MethodSpec onClick = MethodSpec.methodBuilder("onClick")
.addAnnotation(override)//添加注解
.addModifiers(Modifier.PUBLIC)
.addParameter(viewParameter)
.addJavadoc("自动生成代码,勿删")
.addCode("String name = \"xiaoming\";\n\n")
.addStatement("$T good = \"\"",String.class)
.addStatement("$T twoView = null",viewClass)
.addStatement("good = name.$N()","toString")
.addStatement("twoView = new $T(this)",viewClass)
.addStatement("$T lll = $S",String.class,"lllll")
.addStatement("$T tttt = $L",int.class,22)
.build();
MethodSpec test = MethodSpec.methodBuilder("test")
.addModifiers(Modifier.PRIVATE)
.addCode("int count = 0;\n")
.beginControlFlow("for(int i=0;i<5;i++)")
.addStatement("count = count + i")
.endControlFlow()
.build();
生成的代码:
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 自动生成代码,勿删 */
@Override
public void onClick(View view) {
String name = "xiaoming";
String good = "";
View twoView = null;
good = name.toString();
twoView = new View(this);
String lll = "lllll";
int tttt = 22;
}
添加注释:
addJavadoc(String format, Object... args)
.addJavadoc("自动生成代码,勿删")
流程控制:
beginControlFlow,endControlFlow会自动添加{}
MethodSpec test = MethodSpec.methodBuilder("test")
.addModifiers(Modifier.PRIVATE)
.addCode("int count = 0;\n")
.beginControlFlow("for(int i=0;i<5;i++)")
.addStatement("count = count + i")
.endControlFlow()
.build();
private void test() {
int count = 0;
for(int i=0;i<5;i++) {
count = count + i;
}
}
生成文件
JavaFile负责生成最终的java文件内容,可以输出到所需地方,toString会以String输出:
JavaFile.builder(String packageName, TypeSpec typeSpec)
JavaFile通过向build方法传入PackageName(Java文件的包名)、TypeSpec(生成的内容)生成。
javaFile.writeTo(System.out)
生成的内容会输出到控制台中
javaFile.writeTo(File file)
生成的内容会存放到存储的java文件中。
JavaFile file = JavaFile.builder("com.ldx.canvasdrawdemo", mainActivity).build();
mMessager.printMessage(Diagnostic.Kind.NOTE, file.toString()+"");
file.writeTo(mFilerUtils);
3 简单举例
public void createActivity(){
try {
ClassName superClass = ClassName.get("android.support.v7.app","AppCompatActivity");
ClassName onClickClass = ClassName.get("android.view", "View.OnClickListener");
ClassName viewClass = ClassName.get("android.view", "View");
TypeSpec.Builder typeSpecBuilder = TypeSpec.classBuilder("TwoActivity")
.addModifiers(Modifier.PUBLIC)
.superclass(superClass)
.addSuperinterface(onClickClass);
ClassName override = ClassName.get("java.lang", "Override");
ClassName bundle = ClassName.get("android.os", "Bundle");
ClassName nullable = ClassName.get("android.support.annotation", "Nullable");
AnnotationSpec overRideAnno = AnnotationSpec.builder(override).build();
AnnotationSpec nullableAnno = AnnotationSpec.builder(nullable).build();
ParameterSpec savedInstanceState = ParameterSpec.builder(bundle, "savedInstanceState")
.addAnnotation(nullable)
// .addAnnotation(nullableAnno)
.build();
ParameterSpec viewParameter = ParameterSpec.builder(viewClass, "view")
.build();
MethodSpec onCreate = MethodSpec.methodBuilder("onCreate")
.addAnnotation(override)
// .addAnnotation(overRideAnno)
.addModifiers(Modifier.PROTECTED)
.addParameter(savedInstanceState)
.addStatement("super.onCreate(savedInstanceState)")
.addStatement("setContentView(R.layout.activity_main)")
.build();
MethodSpec onClick = MethodSpec.methodBuilder("onClick")
.addAnnotation(override)
.addModifiers(Modifier.PUBLIC)
.addParameter(viewParameter)
.addJavadoc("自动生成代码,勿删")
.addCode("String name = \"xiaoming\";\n\n")
.addStatement("$T good = \"\"",String.class)
.addStatement("$T twoView = null",viewClass)
.addStatement("good = name.$N()","toString")
.addStatement("twoView = new $T(this)",viewClass)
.addStatement("$T lll = $S",String.class,"lllll")
.addStatement("$T tttt = $L",int.class,22)
.build();
MethodSpec test = MethodSpec.methodBuilder("test")
.addModifiers(Modifier.PRIVATE)
.addCode("int count = 0;\n")
.beginControlFlow("for(int i=0;i<5;i++)")
.addStatement("count = count + i")
.endControlFlow()
.build();
Class stringClazz = String.class;
FieldSpec fieldSpec = FieldSpec.builder(stringClazz, "mName", Modifier.PRIVATE).build();
FieldSpec fieldSpec2 = FieldSpec.builder(stringClazz, "mStr2")
.addModifiers(Modifier.PUBLIC)
.build();
TypeSpec mainActivity = typeSpecBuilder.addMethod(onCreate)
.addMethod(onClick)
.addMethod(test)
.addField(fieldSpec)
.addField(fieldSpec2)
.build();
JavaFile file = JavaFile.builder("com.ldx.canvasdrawdemo", mainActivity).build();
mMessager.printMessage(Diagnostic.Kind.NOTE, file.toString()+"");
file.writeTo(mFilerUtils);
} catch (IOException e) {
e.printStackTrace();
}
}
生成文件:
package com.ldx.canvasdrawdemo;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.View.OnClickListener;
import java.lang.Override;
import java.lang.String;
public class TwoActivity extends AppCompatActivity implements View.OnClickListener {
private String mName;
public String mStr2;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
/**
* 自动生成代码,勿删 */
@Override
public void onClick(View view) {
String name = "xiaoming";
String good = "";
View twoView = null;
good = name.toString();
twoView = new View(this);
String lll = "lllll";
int tttt = 22;
}
private void test() {
int count = 0;
for(int i=0;i<5;i++) {
count = count + i;
}
}
}
其他:
匿名内部类:TypeSpec.anonymousInnerClass()
TypeSpec comparator = TypeSpec.anonymousClassBuilder("")
.addSuperinterface(ParameterizedTypeName.get(Comparator.class, String.class))
.addMethod(MethodSpec.methodBuilder("compare")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.addParameter(String.class, "a")
.addParameter(String.class, "b")
.returns(int.class)
.addStatement("return $N.length() - $N.length()", "a", "b")
.build())
.build();
TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addMethod(MethodSpec.methodBuilder("sortByLength")
.addParameter(ParameterizedTypeName.get(List.class, String.class), "strings")
.addStatement("$T.sort($N, $L)", Collections.class, "strings", comparator)
.build())
.build();
生成的文件:
void sortByLength(List strings) {
Collections.sort(strings, new Comparator() {
@Override
public int compare(String a, String b) {
return a.length() - b.length();
}
});
}
更多api参阅:https://github.com/square/javapoet