您的位置:首页 > 移动开发 > Android开发

2.Android注解-编译时生成代码 APT(Annotation Processing Tool ) 实例说明

2017-08-31 13:40 796 查看
项目构建如下

建一个主工程,一个纯注解的anotation java工程,一个编译生成代码的compiler java工程,一个android library库。

compiler java工程不会打包入项目的,只是在编译的时候生成相关代码而已。

|—-

—app(主android项目)

—rulangtool-api(android library)

—rualngtool-annotation(java library)

—rulangtool-commpiler(java library)

工程如上(图片无法上传了,蛋疼啊 只能如此写结构图)

在app工程里面引入的是

compile project(':rulangtool-annotation')
compile project(':rulangtool-api')
// apt project(':rulangtool-compiler')
// 刚开始我写的apt,后修改配置位annotationProcessor
annotationProcessor project(':rulangtool-compiler')


rulangtool-compiler只是用在编译时期生成代码,其他时间并无其他作用的。

rulangtool-annotation 是一个注解java library。

拿我们常用的android butterknife来做说明。举例BindView,Onclick(View)2个注解来说明。

rulangtool-annotation里的代码如下:

注解部分文章

@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface BindView {
int value();
}
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface OnClick {
int[] value();
}


rulangtool-commpiler来说明一下。

工程引入

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':rulangtool-annotation')
compile 'com.squareup:javapoet:1.7.0'
compile 'com.google.auto.service:auto-service:1.0-rc2'
}


javax.lang.model.element 用于 Java 编程语言的模型元素的接口。

AnnotationMirror表示一个注释。

AnnotationValue 表示注释类型元素的值。

AnnotationValueVisitor<R,P>注释类型元素值的 visitor,使用 visitor 设计模式的变体。
Element 表示一个程序元素,比如包、类或者方法。

ElementVisitor<R,P> 程序元素的 visitor,使用 visitor 设计模式的样式。

ExecutableElement 表示某个类或接口的方法、构造方法或初始化程序(静态或实例),包括注释类型元素。

Name 字符的不可变序列。

PackageElement 表示一个包程序元素。

TypeElement 表示一个类或接口程序元素。

TypeParameterElement 表示一般类、接口、方法或构造方法元素的形式类型参数。

VariableElement 表示一个字段、enum 常量、方法或构造方法参数、局部变量或异常参数。


BindViewField

public class BindViewField {
private VariableElement mVariableElement;
private int mresId;

public BindViewField(Element element) throws IllegalArgumentException{
if (element.getKind() != ElementKind.FIELD) {
throw new IllegalArgumentException(String.format("Only fields can be annotated with @%s",
BindView.class.getSimpleName()));
}
mVariableElement = (VariableElement) element;

BindView bindView = mVariableElement.getAnnotation(BindView.class);
mresId = bindView.value();
if (mresId < 0) {
throw new IllegalArgumentException(
String.format("value() in %s for field %s is not valid !", BindView.class.getSimpleName(),
mVariableElement.getSimpleName()));
}
}

/**
* 获取变量名称
* @return
*/
public Name getFieldName() {
return mVariableElement.getSimpleName();
}

/**
* 获取变量id
* @return
*/
public int getResId() {
return mresId;
}

/**
* 获取变量类型
* @return
*/
public TypeMirror getFieldType() {
return mVariableElement.asType();
}

}


OnClickMethod
public class OnClickMethod {

private ExecutableElement mExecutableElement;
private int[] resIds;
private Name mMethodName;

public OnClickMethod(Element element) throws IllegalArgumentException {
if (element.getKind() != ElementKind.METHOD) {
throw new IllegalArgumentException(
String.format("Only methods can be annotated with @%s",
OnClick.class.getSimpleName()));
}

mExecutableElement = (ExecutableElement) element;

resIds = mExecutableElement.getAnnotation(OnClick.class).value();

if (resIds == null) {
throw new IllegalArgumentException(String.format("Must set valid ids for @%s",
OnClick.class.getSimpleName()));
} else {
for (int id : resIds) {
if (id < 0) {
throw new IllegalArgumentException(String.format("Must set valid id for @%s",
OnClick.class.getSimpleName()));
}
}
}
mMethodName = mExecutableElement.getSimpleName();
List<? extends VariableElement> parameters = mExecutableElement.getParameters();

if (parameters.size() > 0) {
throw new IllegalArgumentException(
String.format("The method annotated with @%s must have no parameters",
OnClick.class.getSimpleName()));
}
}

/**
* 获取方法名称
* @return
*/
public Name getMethodName() {
return mMethodName;
}

/**
* 获取id数组
* @return
*/
public int[] getResIds() {
return resIds;
}

}


同一个类里面的注解处理

AnnotatedClass

public class AnnotatedClass {

private TypeElement mTypeElement;
private ArrayList<BindViewField> mFields;
private ArrayList<OnClickMethod> mMethods;
private Elements mElements;

public AnnotatedClass(TypeElement typeElement, Elements elements) {
mTypeElement = typeElement;
mElements = elements;
mFields = new ArrayList<>();
mMethods = new ArrayList<>();
}

public String getFullClassName() {
return mTypeElement.getQualifiedName().toString();
}

public void addField(BindViewField field) {
mFields.add(field);
}

public void addMethod(OnClickMethod method) {
mMethods.add(method);
}

public JavaFile generateFile() {
//generateMethod
MethodSpec.Builder injectMethod = MethodSpec.methodBuilder("inject")
.addModifiers(Modifier.PUBLIC)
.addAnnotation(Override.class)
.addParameter(TypeName.get(mTypeElement.asType()), "host", Modifier.FINAL)
.addParameter(TypeName.OBJECT, "source")
.addParameter(TypeUtil.PROVIDER,"provider");

for(BindViewField field : mFields){
// find views
injectMethod.addStatement("host.$N = ($T)(provider.findView(source, $L))",
field.getFieldName(),
ClassName.get(field.getFieldType()), field.getResId());
}

for(OnClickMethod method :mMethods){
TypeSpec listener = TypeSpec.anonymousClassBuilder("")
.addSuperinterface(TypeUtil.ANDROID_ON_CLICK_LISTENER)
.addMethod(MethodSpec.methodBuilder("onClick")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.VOID)
.addParameter(TypeUtil.ANDROID_VIEW, "view")
.addStatement("host.$N()", method.getMethodName())
.build())
.build();
injectMethod.addStatement("View.OnClickListener listener = $L ", listener);
for (int id : method.getResIds()) {
// set listeners
injectMethod.addStatement("provider.findView(source, $L).setOnClickListener(listener)", id);
}
}

//generaClass
TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + "$$RuLang")
.addModifiers(Modifier.PUBLIC)
.addSuperinterface(ParameterizedTypeName.get(TypeUtil.INJET, TypeName.get(mTypeElement.asType())))
.addMethod(injectMethod.build())
.build();

String packgeName = mElements.getPackageOf(mTypeElement).getQualifiedName().toString();

return JavaFile.builder(packgeName, injectClass).build();
}

}


真正的编译器生成代码咋这个里面:

@AutoService(Processor.class)
public class ViewInjectProcesser extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement>annotations, RoundEnvironment roundEnv) {

}
}


这句代码非常重要 TypeSpec injectClass = TypeSpec.classBuilder(mTypeElement.getSimpleName() + “RuLang"),这是生成java文件的名字:mTypeElement.getSimpleName()+“RuLang”.java

在rulangtool-api里面开始

private static void inject(Object host, Object object, Provider provider) {
String className = host.getClass().getName();
try {
Inject inject = injectMap.get(className);

if (inject == null) {
//利用反射实例话生成的对象,通过注入实例化相关字段或者关联方法。
Class<?> aClass = Class.forName(className + "$$RuLang");
inject = (Inject) aClass.newInstance();
injectMap.put(className, inject);
}
inject.inject(host, object, provider);
} catch (Exception e) {
e.printStackTrace();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐