annotationProcessor和android-apt的功能是一样的,它们是替代关系。annotationProcessor是APT工具中的一种,他是google开发的内置框架,不需要引入,可以直接在build.gradle文件中使用。android-apt是由一位开发者自己开发的apt框架,随着Android Gradle 插件 2.2 版本的发布,间接的进行了替代。
涉及注解相关知识:
@Retention的取值范围如下(代表注解的保留位置):
RetentionPolicy.SOURCE 表示修饰的注解只在源码中保留,编译后就被遗弃了,也就是class文件中就不存在了。
RetentionPolicy.CLASS 表示修饰的注解保留到编译后的class文件,运行时就被遗弃。比如在运行时通过反射去获取这个注解,会发现是不存在的。
RetentionPolicy.RUNTIME 表示注解一直保留到运行时。
@Target:的取值范围如下(代表注解的作用目标)
@Target(ElementType.TYPE)——接口、类、枚举、注解
@Target(ElementType.FIELD)——字段、枚举的常量
@Target(ElementType.METHOD)——方法
@Target(ElementType.PARAMETER)——方法参数
@Target(ElementType.CONSTRUCTOR) ——构造函数
@Target(ElementType.LOCAL_VARIABLE)——局部变量
@Target(ElementType.ANNOTATION_TYPE)——注解
@Target(ElementType.PACKAGE)——包
@Document:该注解可以被包含在javadoc中
@Inherited:子类可以继承父类中的该注解
三方库依赖方式:
implementation:该依赖方式所依赖的库不会传递,只会在当前module中生效。
api:该依赖方式会传递所依赖的库,当其他module依赖了该module时,可以使用该module下使用api依赖的库。
compile已过时,已被 implementation 和api 取代
当我们依赖一些第三方的库时,可能会遇到com.android.support冲突的问题,就是因为开发者使用的compile或api依赖的com.android.support包与我们本地所依赖的com.android.support包版本不一样。
一、定义注解
在工程中添加一个java library类型的module,取名annotation
Android Studio -> file -> new module -> java library
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface BindView {
@IdRes int value();
}
备注:注解@IdRes 引用系统库,如果是jiavaLibrary需要单独添加引用
dependencies {
implementation 'androidx.annotation:annotation:1.2.0'
}
二、定义注解处理器
新建一个module,取名为compiler,类型必须为java librar(因为有用到 javax.*下的类文件)
Android Studio -> file -> new module -> java library
新建一个类MyProcessor
,这类必须继承至 AbstractProcessor
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {
}
备注:注解处理器创建完毕后,需要创建注册目录,如果是手动创建,目录如下:
文件里面内容为自己处理器的全路径path
建议使用google的 AutoService,通过@AutoService注解标记一下,就可以自动完成注册动作,避免了手动创建错误,引用如下:
dependencies {
//google的 AutoService
implementation 'com.google.auto.service:auto-service:1.0-rc2'
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc2'
//自己的注解库
implementation project(':annotation')
}
生成路径:
Processor
常用到的4个方法:
/**
* 初始化常用的工具类
* @param processingEnv
*/
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();//文件操作相关
messager = processingEnv.getMessager();//日志相关
elementUtils = processingEnv.getElementUtils();//元素相关
}
/**
* 支持的注解类型
* 也可以通过系统注解
* @SupportedAnnotationTypes({"com.example.annotation.BindView"})
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(BindView.class.getCanonicalName());
return types;
}
/**
* 核心逻辑处理
* @param annotations
* @param roundEnv
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
roundEnv) {
return true;
}
/**
* 支持的java版本
* 也可以通过系统注解代替
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}
系统注解使用如下:
@SupportedSourceVersion(SourceVersion.RELEASE_7)
@SupportedAnnotationTypes({"com.example.annotation.BindView"})
@AutoService(Processor.class)
public class MyProcessor extends AbstractProcessor {}
如果需要调试传递参数:
defaultConfig {
applicationId "com.example.demo0328"
minSdkVersion 21
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = [xxxkey: 'xxxxvlaue']
}
}
}
注解处理器所在库获取参数:
Map<String, String> options = processingEnv.getOptions();
注解相关的api可以参考:注解与APT注解处理器技术详解 - 简书
三、新建android library:inject
public interface ViewBinder<T> {
void bind(T target);
}
public class InjectView {
public static void bind(Activity activity) {
String className = activity.getClass().getName();
try {
// 得到我们生成的对应该Activity的ViewBinder类的Class对象
Class<?> viewBinderClass = Class.forName(className + "$$ViewBinder");
ViewBinder viewBinder = (ViewBinder) viewBinderClass.newInstance();
// 绑定Activity的控件
viewBinder.bind(activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
引用如下:
api project(':annotation')
接下来开始生成代码文件了。。。
代码生成
生成java文件工具有:Filer(注解处理器自带)、JavaPoet(省去手工拼接字符串麻烦)
手工生成api如下:
private void saveFile(String pkNameQ, String content) {
String pkName = pkNameQ;
try {
//创建java类文件
JavaFileObject jfo = filer.createSourceFile(pkName + ".ViewBindId", new Element[]{});
Writer writer = jfo.openWriter();
writer.write(writeCode(pkName, content));
writer.flush();
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
private String writeCode(String pkName, String content) {
StringBuilder builder = new StringBuilder();
builder.append("package " + pkName + ";\n\n");
builder.append("import java.io.Closeable;\n");
builder.append("import java.io.IOException;\n");
builder.append("import android.util.Log;\n\n");
builder.append("public class ViewBindId implements Closeable { \n\n");
//生成方法 1
builder.append("public static void setMsg(String[] args){ \n");
builder.append("for (int i = 0; i < args.length; i++) { \n");
builder.append("System.out.println(\"内容\"+args[i]);");
builder.append("Log.i(\"wangsen\",\"内容:\"+args[i]);");
builder.append("}\n");
builder.append("System.out.println(\"" + content + "\");\n");
builder.append("}\n");
//生成方法 2
builder.append("@Override\n");
builder.append("public void close() throws IOException { \n");
builder.append("System.out.println(\"" + content + "close\");\n");
builder.append("}\n");
builder.append("}");
return builder.toString();
}
拼接步骤麻烦,比如说换行符、导包操作等等。
javaPoet生成java文件,api如下:
常用的api:
- addStatement() 方法负责分号和换行
- beginControlFlow() + endControlFlow() 需要一起使用,提供换行符和缩进。
- addCode() 以字符串的形式添加内
- returns 添加返回值类型
- .constructorBuilder() 生成构造器函数
- .addAnnotation 添加注解
- addSuperinterface 给类添加实现的接口
- superclass 给类添加继承的父类
- ClassName.bestGuess(“类全名称”) 返回ClassName对象,这里的类全名称表示的类必须要存在,会自动导入相应的包
- ClassName.get(“包名”,”类名”) 返回ClassName对象,不检查该类是否存在
- TypeSpec.interfaceBuilder(“HelloWorld”)生成一个HelloWorld接口
- MethodSpec.constructorBuilder() 构造器
- addTypeVariable(TypeVariableName.get(“T”, typeClassName))
会给生成的类加上泛型
占位符
- $L代表的是字面量
- $S for Strings
- $N for Names(我们自己生成的方法名或者变量名等等)
- $T for Types
具体api使用,可以参考,感谢感谢,写的挺好的:
JavaPoet使用详解_wings专栏的博客-优快云博客_javapoet
代码生成如下:
private void autoSaveFile(String pkName, String activityName, ClassName activityClass,
String bindViewFiledName, int resId) {
// 创建方法
MethodSpec main = MethodSpec.methodBuilder("bind")
.addModifiers(Modifier.PUBLIC)//
.returns(void.class)
.addAnnotation(Override.class)
.addParameter(activityClass, "target")
.addStatement("$T.out.println($S)", System.class, "自动创建的")
.addStatement("target.$L = target.findViewById($L)", bindViewFiledName, resId)
.build();
FieldSpec fieldSpec = FieldSpec.builder(int.class,"age", Modifier.PUBLIC).build();
ClassName viewBinderClass = ClassName.get("com.example.inject", "ViewBinder");
// 创建类
TypeSpec ViewBinder = TypeSpec.classBuilder(activityName + "$$ViewBinder")//
.addModifiers(Modifier.PUBLIC, Modifier.FINAL)//
.addMethod(main)//
.addField(String.class, "sex", Modifier.PRIVATE)//增加成员变量
.addField(fieldSpec)
.addSuperinterface(ParameterizedTypeName.get(viewBinderClass, activityClass))
.build();
//String packageName = processingEnv.getElementUtils().getPackageOf(element).getQualifiedName().toString();
try {
//生成java文件
JavaFile javaFile = JavaFile.builder(pkName, ViewBinder)//
.addFileComment(" This codes are generated automatically. Do not modify!")//
.build();
javaFile.writeTo(filer);
} catch (IOException e) {
e.printStackTrace();
}
}
调用如下:
public class MainActivity extends AppCompatActivity {
//关键
@BindView(R.id.tv01)
TextView tv01;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//关键
InjectView.bind(this);
tv01.setText(Html.fromHtml("DD<font color='red'>注入初始化*</font>"));
ViewBindId.setMsg(new String[]{"a","b","c"});
ViewBinderAuto.setMsg(new String[]{"D","E","F"});
try {
new ViewBindId().close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
切记build.gradle添加相关引用:
implementation project(':inject') //implementation project(':annotation') annotationProcessor project(path: ':compiler')
手写拼接可以参考:EventBus,Dragger等开源库的基本原理。自动生成参考butterknife
参考:
Android关于AutoService、Javapoet讲解 - 帅气的码农 - 博客园
源码解析getCanonicalName(), getName(), getSimpleName()的不同_正在飞翔的猫的博客-优快云博客_getcanonicalname
implementation、api、compileOnly区别详解_XeonYu的博客-优快云博客_compileonly implementation
深入理解编译注解(二)annotationProcessor与android-apt_珠穆朗玛小王子的博客-优快云博客
注解入坑笔记:关于注解使用必须了解的——Annotation、AbstraceProcessor、APT_Else_Q的博客-优快云博客
秒懂Android注解处理器(Android Annotation Processor)-蒲公英云