apt:
@Retention后面的值,设置的为CLASS,说明就是编译时动态处理的。一般这类注解会在编译的时候,根据注解标识,动态生成一些类或者生成一些xml都可以,在运行时期,这类注解是没有的~~会依靠动态生成的类做一些操作,因为没有反射,效率和直接调用方法没什么区别~~~
RUNTIME, 说明就是运行时动态处理,这个大家见得应该最多,在运行时拿到类的Class对象,然后遍历其方法、变量,判断有无注解声明,然后做一些事情。
SOURCE,标记一些信息,这么说可能太抽象,那么我说,你见过@Override、@SuppressWarnings等,这类注解就是用于标识,可以用作一些检验
@Target表示该注解可以用于什么地方,可能的类型TYPE(类),FIELD(成员变量)
public enum ElementType {
/**
* Class, interface or enum declaration.
*/
TYPE,
/**
* Field declaration.
*/
FIELD,
/**
* Method declaration.
*/
METHOD,
/**
* Parameter declaration.
*/
PARAMETER,
/**
* Constructor declaration.
*/
CONSTRUCTOR,
/**
* Local variable declaration.
*/
LOCAL_VARIABLE,
/**
* Annotation type declaration.
*/
ANNOTATION_TYPE,
/**
* Package declaration.
*/
PACKAGE
}
TypeElement :类
TypeSpec是类型元素的抽象,通过Kind枚举定义class、interface、enum、annotation四种类型。
MethodSpec代表方法的抽象。
// 元素操作的辅助类
Elements elementUtils;
//文件相关的辅助类
private Filer mFiler;
//日志相关的辅助类
private Messager mMessager;
1、先添加两个java library,一个annotation,一个compiler:
annotation library:用于放置注解。
build.gradle文件:注意属于java library,不使用android library。
apply plugin: 'java' sourceCompatibility = 1.7
targetCompatibility = 1.7
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
}
compile library:用于放置根据注解生成代码类。
build.gradle文件:注意属于java library,不使用android library。
apply plugin: 'java' sourceCompatibility = 1.7
targetCompatibility = 1.7
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.google.auto.service:auto-service:1.0-rc2'
compile 'com.squareup:javapoet:1.7.0'
<!-- 引用annotation library -->
compile project(':annotation')
}
app module:android app
build.gradle文件:
apply plugin: 'com.android.application'
apply plugin: 'com.neenbedankt.android-apt' android {
compileSdkVersion 23
buildToolsVersion "23.0.2" defaultConfig {
applicationId "com.example.aptdemo"
minSdkVersion 15
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
} dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:24.0.0' compile project(':annotation')
apt project(':compiler')
}
官方有一个列子的:生成一个java类,然后java 类里面有个main函数:
// 注解类 (目录:annotation library)
package com.example; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; @Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface Test {
}
// apt生成类 (目录:compiler library)
package com.example; import com.google.auto.service.AutoService;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;
import java.io.IOException;
import java.util.Collections;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement; @AutoService(Processor.class)
public class TestProcessor extends AbstractProcessor { /***
package com.example.helloworld; public final class HelloWorld {
public static void main(String[] args) {
System.out.println("Hello, JavaPoet!");
}
}
*/ @Override
public Set<String> getSupportedAnnotationTypes() {
return Collections.singleton(Test.class.getCanonicalName());
} @Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { MethodSpec main = MethodSpec.methodBuilder("main")
.addModifiers(Modifier.PUBLIC, Modifier.STATIC)
.returns(void.class)
.addParameter(String[].class, "args")
.addStatement("$T.out.println($S)", System.class, "Hello, JavaPoet!")
.build(); TypeSpec helloWorld = TypeSpec.classBuilder("HelloWorld")
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethod(main)
.build(); JavaFile javaFile = JavaFile.builder("com.example.helloworld", helloWorld)
.build(); try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
} return false;
} @Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
}
官方例子
// 使用:
在随意一个类上面加上@Test,编译时,代码就会生成。
下面就是简单的view注解生成代码:
// 注解类 (目录:annotation library)
1、activity 注解 :作用于类,所以目标类型是类。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface DIActivity {
}
2、view 注解:作用于字段,所以目标类型是字段。
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DIView { int value() default 0;
}
3、onClick 注解:作用于方法,所以目标类型是方法。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DIOnClick { int[] value() default 0;
}
// 使用工具类:
ViewInject:用于统一activity 使用方法,注入。
public class ViewInject { public final static void bind(Activity activity){ String className = activity.getClass().getName();
try {
Class<?> aClass = Class.forName(className + "$ViewInject");
Inject inject = (Inject) aClass.newInstance();
inject.bindView(activity);
} catch (Exception e){
e.printStackTrace();
}
}
}
Inject:用于apt生成类继承,统一父类。
public interface Inject<T> { void bindView(T host);
}
// apt生成类 (目录:compiler library)
DIProcessor:
@AutoService(Processor.class)
public class DIProcessor extends AbstractProcessor { // 元素操作的辅助类
Elements elementUtils;
//文件相关的辅助类
private Filer mFiler;
//日志相关的辅助类
private Messager mMessager; @Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
// 元素操作的辅助类
elementUtils = processingEnv.getElementUtils();
mFiler = processingEnv.getFiler();
mMessager = processingEnv.getMessager();
} /**
* 指定哪些注解应该被注解处理器注册
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
/**
* Set<String> types = new LinkedHashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(OnClick.class.getCanonicalName());
return types;
* */
// 规定需要处理的注解
return Collections.singleton(DIActivity.class.getCanonicalName());
} @Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(DIActivity.class); for(Element element : elements){ // 判断是否Class
TypeElement typeElement = (TypeElement) element; List<? extends Element> members = elementUtils.getAllMembers(typeElement); MethodSpec.Builder bindViewMethod = MethodSpec.methodBuilder("bindView")
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.VOID)
.addParameter(ClassName.get(typeElement.asType()), "activity", Modifier.FINAL); bindViewMethod.addStatement("mContext = activity"); MethodSpec.Builder onClickMethod = MethodSpec.methodBuilder("onClick")
.addAnnotation(Override.class)
.addModifiers(Modifier.PUBLIC)
.returns(TypeName.VOID)
.addParameter(ClassName.get("android.view", "View"), "view"); // TypeSpec.Builder listener = TypeSpec.anonymousClassBuilder("")
// .addSuperinterface(ClassName.get("android.view", "View", "OnClickListener")); mMessager.printMessage(Diagnostic.Kind.NOTE, "非常厉害"); // bindViewMethod.addStatement("View.OnClickListener listener = null");
onClickMethod.addCode("switch(view.getId()) {"); for (Element item : members) {
// handler the findViewById
DIView diView = item.getAnnotation(DIView.class);
if (diView != null){ bindViewMethod.addStatement(String.format("activity.%s = (%s) activity.findViewById(%s)"
,item.getSimpleName(), ClassName.get(item.asType()).toString(), diView.value()));
continue;
} // handler the setOnViewClick
DIOnClick diOnClick = item.getAnnotation(DIOnClick.class);
if(diOnClick != null) { if(diOnClick.value().length > 1) {
for (int index = 0; index < diOnClick.value().length; index++) {
onClickMethod.addCode("case $L: ", diOnClick.value()[index]); bindViewMethod.addStatement("activity.findViewById($L).setOnClickListener(this)", diOnClick.value()[index]);
} onClickMethod.addStatement("mContext.$N()", item.getSimpleName()); onClickMethod.addStatement("break");
} if(diOnClick.value().length == 1) {
onClickMethod.addCode("case $L: ", diOnClick.value()[0]);
onClickMethod.addStatement("mContext.$N()", item.getSimpleName());
onClickMethod.addStatement("break"); bindViewMethod.addStatement("activity.findViewById($L).setOnClickListener(this)", diOnClick.value()[0]);
}
continue;
}
} onClickMethod.addStatement("}"); // TypeSpec onClickListener = listener.addMethod(onClickMethod.build()).build();
// bindViewMethod.addStatement("listener = $L ", onClickListener); List<MethodSpec> methodSpecs = new ArrayList<>();
methodSpecs.add(bindViewMethod.build());
methodSpecs.add(onClickMethod.build()); List<TypeName> typeNames = new ArrayList<>();
typeNames.add(ClassName.get("android.view", "View", "OnClickListener"));
typeNames.add(ParameterizedTypeName.get(ClassName.get("com.example.charles.aptdemo.inject", "Inject"), TypeName.get(typeElement.asType()))); FieldSpec fieldSpec = FieldSpec.builder(TypeName.get(typeElement.asType()), "mContext").build(); TypeSpec typeSpec = TypeSpec.classBuilder(element.getSimpleName() + "$ViewInject")
// .superclass(TypeName.get(typeElement.asType()))
.addSuperinterfaces(typeNames)
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)
.addMethods(methodSpecs)
.addField(fieldSpec)
.build(); JavaFile javaFile = JavaFile.builder(getPackageName(typeElement), typeSpec).build(); try {
javaFile.writeTo(processingEnv.getFiler());
} catch (IOException e) {
e.printStackTrace();
} mMessager.printMessage(Diagnostic.Kind.NOTE, "完成");
} return true;
} private String getPackageName(TypeElement type) {
return elementUtils.getPackageOf(type).getQualifiedName().toString();
} /**
* 指定使用的 Java 版本。通常返回SourceVersion.latestSupported()。
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
}
DIProcessor
// 使用方式:
@DIActivity
public class MainActivity extends AppCompatActivity { @DIView(R.id.text)
public TextView textView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); ViewInject.bind(this); textView.setText("我不是这样子的");
} @DIOnClick({R.id.btn_change_text, R.id.btn_change})
public void changeTextView(){
textView.setText("我被点击了");
} @DIOnClick(R.id.btn_change_new)
public void changeNew(){
textView.setText("new");
}
}
关于javapoet:
JavaPoet
是一个用来生成 .java源文件的Java API。
下列文章为apt
下列文章为反射: