apt 根据注解,编译时生成代码
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
下列文章为反射:
Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)
Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)
apt 根据注解,编译时生成代码的更多相关文章
- 利用APT实现Android编译时注解
摘要: 一.APT概述 我们在前面的java注解详解一文中已经讲过,可以在运行时利用反射机制运行处理注解.其实,我们还可以在编译时处理注解,这就是不得不说官方为我们提供的注解处理工具APT (Anno ...
- .net postsharp编译时生成的代码?
使用PostSharp进行AOP框架设计:一个简单的原型 AOP已经不是一个什么新名词了,在博客园使用关键字搜索可以查出n多条关于AOP的介绍,这里就不再赘述了. 在Bruce Zhang's B ...
- qmake.exe是在Qt安装编译时生成的,里面内嵌了Qt相关的一些路径(最简单的方法是保持一样的安装路径,最方便的办法是设置qt.conf文件)
在网上直接下载别人编译好的Qt库,为自己使用省了不少事.但往往也会遇到些问题,其中Qt version is not properly installed,please run make instal ...
- React系列文章:Babel编译JSX生成代码
上次我们总结了React代码构建后的Webpack模块组织关系,今天来介绍一下Babel编译JSX生成目标代码的一些规则,并且模拟整个生成的过程. 我们还是拿最简单的代码举例: import {gre ...
- 【Andrioid】在Gradle编译时生成一个不同的版本号,动态设置应用程序标题,应用程序图标,更换常数
写项目的时候常常会遇到下面的情况: 1.须要生成測试版本号和正式版本号的apk 2.測试版本号和正式版本号的URL是不一样的 3.測试版本号和正式版本号的包名须要不一致,这样才干安装到同一部手机上面. ...
- 使用编译时注解简单实现类似 ButterKnife 的效果
这篇文章是学习鸿洋前辈的 Android 如何编写基于编译时注解的项目 的笔记,用于记录我的学习收获. 读完本文你将了解: 什么是编译时注解 APT 编译时注解如何使用与编写 举个例子 思路 创建注解 ...
- C# 9 新特性:代码生成器、编译时反射
前言 今天 .NET 官方博客宣布 C# 9 Source Generators 第一个预览版发布,这是一个用户已经喊了快 5 年特性,今天终于发布了. 简介 Source Generators 顾名 ...
- Android 编译时注解解析框架
2.注解 说道注解,竟然还有各种分类,得,这记不住,我们从注解的作用来反推其分类,帮助大家记忆,然后举例强化大家的记忆,话说注解的作用: 1.标记一些信息,这么说可能太抽象,那么我说,你见过@Over ...
- Android 打造编译时注解解析框架 这只是一个开始
转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43452969 ,本文出自:[张鸿洋的博客] 1.概述 记得很久以前,写过几篇博客 ...
随机推荐
- JS学习:第一周——NO.4继承
1.[关于call] 作用:是用来改变this指向的,有两种参数 第一种:第一个参数,用来改变this指向 第二种:给call前面的函数传参,从第二个参数开始,给call前面的函数从左到右一个个的传参 ...
- 最新版 chrome 33中,backgroundPosition 改了.
ctrl.css('backgroundPosition' 返回值不一样, 原来是:-75px 0px 现在是:left 75px top 0px // chrome 33 返回的是 left 0px ...
- 360随身wifi在win10中连不上网络
找到服务"Wired AutoConfig"和"WLAN AutoConfig"项,点击"启动"按钮,确保使其正常启动. 讲本地网卡共享到移 ...
- 使用野狗(Wilddog)云setValue写入数据
- (void)viewDidLoad { [super viewDidLoad]; //创建野狗实例化对象 用于随时监听数值变化 Wilddog *myRootRef = [[Wilddog all ...
- MySQL存储过程动态SQL语句的生成
用Mysql存储过程来完成动态SQL语句,使用存储过程有很好的执行效率: 现在有要求如下:根据输入的年份.国家.节假日类型查询一个节假日,我们可以使用一般的SQL语句嵌入到Java代码中,但是执行效率 ...
- LeetCode 368
题目描述: Given a set of distinct positive integers, find the largest subset such that every pair (Si, S ...
- Golang 语法学习笔记
Golang 语法学习笔记 包.变量和函数. 包 每个 Go 程序都是由包组成的. 程序运行的入口是包 main. 包名与导入路径的最后一个目录一致."math/rand" 包由 ...
- 【Java远程debug】
转自 http://blog.csdn.net/hongchangfirst/article/details/44191925 一.远程debug原理 Java远程调试的原理是两个JVM之间通过deb ...
- Microsoft Visual Studio PDB文件相关事宜
Microsoft Visual Studio PDB:调试的符号文件,程序数据库 (PDB) 文件保存着调试和项目状态信息,使用这些信息可以对程序的调试配置: 当以 /ZI 或 /Zi(用于 C/C ...
- Visual Studio前端开发工具/扩展
怎么样让Visual Studio更好地编写HTML5, CSS3, JavaScript, jQuery,换句话说就是如何更好地做前端开发.Visual Studio 2010不管是旗舰版还是免费版 ...