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  :类

JavaPoet源码初探

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注解-编译时生成代码 (APT)

下列文章为反射:

Android 打造编译时注解解析框架 这只是一个开始

Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (上)

Android 进阶 教你打造 Android 中的 IOC 框架 【ViewInject】 (下)

apt 根据注解,编译时生成代码的更多相关文章

  1. 利用APT实现Android编译时注解

    摘要: 一.APT概述 我们在前面的java注解详解一文中已经讲过,可以在运行时利用反射机制运行处理注解.其实,我们还可以在编译时处理注解,这就是不得不说官方为我们提供的注解处理工具APT (Anno ...

  2. .net postsharp编译时生成的代码?

    使用PostSharp进行AOP框架设计:一个简单的原型   AOP已经不是一个什么新名词了,在博客园使用关键字搜索可以查出n多条关于AOP的介绍,这里就不再赘述了. 在Bruce Zhang's B ...

  3. qmake.exe是在Qt安装编译时生成的,里面内嵌了Qt相关的一些路径(最简单的方法是保持一样的安装路径,最方便的办法是设置qt.conf文件)

    在网上直接下载别人编译好的Qt库,为自己使用省了不少事.但往往也会遇到些问题,其中Qt version is not properly installed,please run make instal ...

  4. React系列文章:Babel编译JSX生成代码

    上次我们总结了React代码构建后的Webpack模块组织关系,今天来介绍一下Babel编译JSX生成目标代码的一些规则,并且模拟整个生成的过程. 我们还是拿最简单的代码举例: import {gre ...

  5. 【Andrioid】在Gradle编译时生成一个不同的版本号,动态设置应用程序标题,应用程序图标,更换常数

    写项目的时候常常会遇到下面的情况: 1.须要生成測试版本号和正式版本号的apk 2.測试版本号和正式版本号的URL是不一样的 3.測试版本号和正式版本号的包名须要不一致,这样才干安装到同一部手机上面. ...

  6. 使用编译时注解简单实现类似 ButterKnife 的效果

    这篇文章是学习鸿洋前辈的 Android 如何编写基于编译时注解的项目 的笔记,用于记录我的学习收获. 读完本文你将了解: 什么是编译时注解 APT 编译时注解如何使用与编写 举个例子 思路 创建注解 ...

  7. C# 9 新特性:代码生成器、编译时反射

    前言 今天 .NET 官方博客宣布 C# 9 Source Generators 第一个预览版发布,这是一个用户已经喊了快 5 年特性,今天终于发布了. 简介 Source Generators 顾名 ...

  8. Android 编译时注解解析框架

    2.注解 说道注解,竟然还有各种分类,得,这记不住,我们从注解的作用来反推其分类,帮助大家记忆,然后举例强化大家的记忆,话说注解的作用: 1.标记一些信息,这么说可能太抽象,那么我说,你见过@Over ...

  9. Android 打造编译时注解解析框架 这只是一个开始

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/43452969 ,本文出自:[张鸿洋的博客] 1.概述 记得很久以前,写过几篇博客 ...

随机推荐

  1. (转)高效的将excel导入sqlserver中

    大部分人都知道用oledb来读取数据到dataset,但是读取之后怎么处理dataset就千奇百怪了.很多人通过循环来拼接sql,这样做不但容易出错而且效率低下,System.Data.SqlClie ...

  2. excel 2010 学习笔记一 Vlookup 函数的使用

    有这么一句话说的好:在商用场合里,能证明你会基本的EXCEL操作技巧的两个检查标准就是会不会用VLOOKUP函数以及数据透视表功能,那么今天就来总结一下VLOOKUP的一些简单实用的功能. 1.VLO ...

  3. 天河微信小程序入门:阿里云tomcat免费配置https

    天河君在第一时间通过了微信小程序验证,开启了我的微信小程序之旅.因为天河君之前是一名后端狗,对前端不是很了解,所以几乎可以认为是从零开始学做微信小程序.也希望有志在微信小程序方向做点事情的朋友能够和我 ...

  4. Integer与int的区别

    简述:int与Integer的区别: 对于它们,我们可能只是知道简单的区别.Integer是int的一个封装类,int的初始值为0,而Integer的初始值为null.但是他们之间真的仅仅只有这些区别 ...

  5. windows 上vmare超卡的问题解决方案

    http://www.cnblogs.com/jlwen/p/3553722.html

  6. vi编辑器使用

    显示行号 set nu 取消行号 set nonu 定位到某一行 gg 定位到首行 G 定位到最后一行 在VI编辑器中切换调用外部shell命令 :!ifconfig   在编辑过程中,看ip地址 插 ...

  7. JAVA基础学习——1.2 环境搭建 之eclipse安装及中文化

    安装好jdk,配置好环境变量以后,下面就可以进行安装eclipse了. 闲话少说,eclipse下载地址:http://www.eclipse.org/downloads/ 不大用关注checksum ...

  8. 出售Illustrator脚本插件面板(包含面板源码,以及面板上所有的功能源码)

    出售Illustrator脚本插件面板(包含面板源码,以及面板上所有的功能源码) 购买后可提供相应的小修改,以及教你使用往这个多列面里再加上按钮功能! 这套源码可作为工作使用,也可用为新手学习AI脚面 ...

  9. Installing Selenium and ChromeDriver on Ubuntu

    I recently got Selenium, Google Chrome, and ChromeDriver installed and working on a DigitalOcean ins ...

  10. js中JSON格式数据的转化

    JSON.parse(STRING) => OBJECT JSON.stringify(OBJECT) => STRING