一、编译时技术简介
APT ( Annotation Processing Tool ) 注解处理工具 ;
编译时技术 , 广泛应用在当前主流框架中 , 如 JetPack 中的 DataBinding , Room , Navigatoion , 第三方 ButterKnife , ARouter 等框架 ;
编译时技术 最重要的作用就是在编译时可以 生成模板代码 ;
由于生成代码操作是在编译时进行的 , 不会对运行时的性能产生影响 ;
程序的周期 :
源码期 : 开发时 , 刚编写完 " .java " 代码 , 还未编译之前 , 就处于源码期 ;
编译期 : 程序由 java 源码编译成 class 字节码文件 ;
运行期 : 将字节码文件加载到 Java 虚拟机中运行 ;
编译时技术 APT 作用于 编译期 , 在这个过程中使用该技术 , 生成代码 ;
编译时技术 2 22 大核心要素 : 在编译时 , 执行生成代码的逻辑 , 涉及到两个重要概念 ;
① 编译时注解 ;
② 注解处理器 ;
举例说明 : 使用 ButterKnife 时会依赖两个库 ,
dependencies {
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
}
其中
com.jakewharton:butterknife:10.2.3 是 编译时的注解 ,
com.jakewharton:butterknife-compiler:10.2.3 是 注解处理器 ;
二、ButterKnife 原理分析
使用 ButterKnife :
① 添加依赖 :
dependencies {
implementation 'com.jakewharton:butterknife:10.2.3'
annotationProcessor 'com.jakewharton:butterknife-compiler:10.2.3'
}
② Activity 中使用 ButterKnife :
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import butterknife.BindView;
import butterknife.ButterKnife;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tv1.setText("ButterKnife");
}
}
BindView 注解分析 : 在 TextView tv1成员变量处添加了 @BindView(R.id.tv1) 注解 ;
@Target(FIELD) 元注解 : 表示其作用与类的成员字段 ;
@Retention(RUNTIME) 元注解 : 表示该注解保留到运行时阶段 ;
int value() 注解属性 : 只有一个注解属性 , 并且属性名是 value , 则使用注解时 “value =” 可省略 ;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) //注解的生命周期,保留时间到类文件级别
@Target(ElementType.FIELD) //作用域在字段上
public @interface BindView {
int value();
}
TextView tv1 需要使用 findViewById 进行赋值 , 在上述代码中没有写 findViewById 相关的代码 ; 肯定是在某个地方执行了 findViewById 的方法 ;
ButterKnife.bind(this) 代码就是执行了 findViewById 方法 ;
ButterKnife 用到了编译时技术会在项目编译时 , 会生成 MainActivity_ViewBinding 类 , 在该类中 , 会查找添加了 @BindView 直接的成员变量 , 再获取 注解属性 value 的值 , 然后调用 findViewById 方法获取组件并为成员变量赋值 ;
// Generated code from Butter Knife. Do not modify!
import android.view.View;
import android.widget.TextView;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import butterknife.Unbinder;
import butterknife.internal.Utils;
import java.lang.IllegalStateException;
import java.lang.Override;
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(MainActivity target, View source) {
this.target = target;
target.tv1= Utils.findRequiredViewAsType(source, R.id.tv1, "field 'tv1'", TextView.class);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.hello = null;
}
}
三、ButterKnife 生成 Activity_ViewBinding 代码分析
调用 ButterKnife 静态方法 Unbinder bind(@NonNull Activity target) , 传入 Activity 对象 , 在方法中调用了 ButterKnife 的 bind 方法 ;
在 bind 方法中 , 先获取了 Activity 的类对象 ,
Class<?> targetClass = target.getClass();
然后将类对象传入了 findBindingConstructorForClass 方法 ,
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
在 findBindingConstructorForClass 方法中 , 获取了某个构造方法 ,
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
获取 Activity 类对象名称 , 即 " com.butterknife.demo.MainActivity " ,
String clsName = cls.getName();
得到名称后 , 判断该类对象是否是系统的 API , 如果是则退出 ; 如果不是 , 则继续向下执行 ,
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
拼装要生成的类名称 , “com.butterknife.demo.MainActivity_ViewBinding” , 并自动生成该类 ;
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
ButterKnife 涉及到的源码 :
public final class ButterKnife {
/**
* BindView annotated fields and methods in the specified {@link Activity}. The current content
* view is used as the view root.
*
* @param target Target activity for view binding.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Activity target) {
View sourceView = target.getWindow().getDecorView();
return bind(target, sourceView);
}
/**
* BindView annotated fields and methods in the specified {@code target} using the {@code source}
* {@link View} as the view root.
*
* @param target Target class for view binding.
* @param source View root on which IDs will be looked up.
*/
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
@Nullable @CheckResult @UiThread
private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) {
Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls);
if (bindingCtor != null || BINDINGS.containsKey(cls)) {
if (debug) Log.d(TAG, "HIT: Cached in binding map.");
return bindingCtor;
}
String clsName = cls.getName();
if (clsName.startsWith("android.") || clsName.startsWith("java.")
|| clsName.startsWith("androidx.")) {
if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search.");
return null;
}
try {
Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding");
//noinspection unchecked
bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class);
if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor.");
} catch (ClassNotFoundException e) {
if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName());
bindingCtor = findBindingConstructorForClass(cls.getSuperclass());
} catch (NoSuchMethodException e) {
throw new RuntimeException("Unable to find binding constructor for " + clsName, e);
}
BINDINGS.put(cls, bindingCtor);
return bindingCtor;
}
}
了解了butterKnife原理之后,接下来字节手写butterknife注解实现。
四、创建 编译时注解 和 注解处理器
4.1 使用 Android Studio 开发 Android 项目时 , 使用到编译时技术 , 都要用到 编译时注解 和 注解处理器 ;
编译时注解 和 注解处理器 一般都创建为 Java or Kotlin Library 类型的 Module ;
右键点击工程名 , 选择 " New / Module " 选项 ,
在弹出的 " Create New Module " 对话框中 , 这里选择 Module 的类型为 Java or Kotlin Library ;
设置编译时注解 依赖库名称 annotation , 注意语言选择 Java ; 暂时不涉及 Kotlin 注解 ;
使用上述相同的方式 , 创建 annotation-compiler 注解处理器 依赖库 , 这两个 Module 的类型都是 " Java or Kotlin Library " ;
4.2 接下来为项目app模块添加annotation和annotation-compiler模块的依赖
在主应用 " app " 中 , 依赖上述 annotation 编译时注解 依赖库 和 annotation-compiler 注解处理器 依赖库 ;
右键点击应用 , 选择 " Open Modules Settings " 选项 ,
在 " Project Structure " 对话框中选择 " Dependencies " 选项卡 , 选择主应用 " app " , 点击 " + " 按钮 , 选择添加 " Module Dependency " 依赖库 ,
将 annotation 编译时注解 依赖库 和 annotation-compiler 注解处理器 依赖库 添加到主应用 " app " 的依赖中 ;
添加依赖完成 ;
点击 " OK " 按钮后 , 在 build.gradle 构建脚本中自动生成的依赖 :
dependencies {
implementation project(path: ':annotation-compiler')
implementation project(path: ':annotation')
}
注意 : 对于 annotation-compiler 注解处理器 依赖库 不能使用 implementation , 必须使用 annotationProcessor ,
dependencies {
annotationProcessor project(path: ':annotation-compiler')
implementation project(path: ':annotation')
}
五、开发 annotation 编译时注解 依赖库 ;
创建BindView.java注解类
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.CLASS) //注解的生命周期,保留时间到类文件级别
@Target(ElementType.FIELD) //作用域在字段上
public @interface BindView {
int value();
}
六、开发 annotation-compiler 注解处理器 依赖库 ;
6.1 创建 AnnotationCompiler 注解处理器 , 该类主要作用是生成代码 , 注解处理器类必须继承 javax.annotation.processing.AbstractProcessor 类 , 这是 Java 的 API , 再 Android 中无法获取该 API , 因此 编译时注解 和 注解处理器 都必须是 Java 依赖库 ;
创建AnnotationCompiler.java
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
/**
* 生成代码的注解处理器
*/
public class AnnotationCompiler extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
6.2 使用注解 @AutoService(Processor.class) 标注 注解处理器
上述实现的 AbstractProcessor 中的 process 方法 , 专门用于搜索 Android 源码中使用的 编译时注解 的方法 ;
程序构建时 , 发现了 annotationProcessor project(path: ':annotation-compiler') 依赖 , 使用 annotationProcessor 进行依赖 , 说明这是一个注解处理器 , 此时会到 annotation-compiler 模块中查找 注解处理器 , 注解处理器 的查找方式也是 通过 注解 标记 进行查找 , 该注解是 Google 服务库提供的 ;
如果要使用 注解 标记注解处理器 , 首先要依赖 Google 服务库 ,
在 annotation-compiler模块的build.gradle中添加
dependencies {
//使用google的auto-service依赖库
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation 'com.google.auto.service:auto-service:1.0-rc6'
}
然后到 public class Compiler extends AbstractProcessor 注解处理器类上使用 @AutoService(Processor.class) 标注 , Android 编译器通过查找该注解 , 确定哪个类是注解处理器 ;
import java.util.Set;
import com.google.auto.service.AutoService;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.element.TypeElement;
/**
* 生成代码的注解处理器
*/
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
return false;
}
}
6.3 注解处理器 init 初始化方法
AbstractProcessor 注解处理器中 , 专门提供了一个 init 方法 , 该方法专门用于注解处理器相关初始化操作 ;
init 方法的 ProcessingEnvironment processingEnv 参数很重要 , 通过该参数可以获取 注解处理器中的各种重要工具 ;
ProcessingEnvironment 中定义了获取相关工具的接口 ;
public interface ProcessingEnvironment {
Map<String, String> getOptions();
Messager getMessager();
Filer getFiler();
Elements getElementUtils();
Types getTypeUtils();
SourceVersion getSourceVersion();
Locale getLocale();
}
6.4 注解处理器 Filer 代码生成工具具
(1)通过注解生成 Java 代码需要使用 Filer 对象 , 将该对象定义到 注解处理器 成员变量中 , 在 init 方法中进行初始化操作 ;
通过 ProcessingEnvironment 可以通过调用 getFiler 方法 , 获取 Filer 对象 ;
(2)注解处理器中不能打断点进行调试 , 也不能使用 Log , System.out 打印日志 , 在注解处理器中只能通过 Messager 打印日志 ;
通过调用 ProcessingEnvironment 的 getMessager 方法 , 可以获取到 Messager 对象 ;
//生成文件的对象
private Filer filer;
//日志打印
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
七、注解处理器annotation-compiler 依赖 编译时注解annotation模块
注解处理器 需要处理 编译时注解 , 因此必须能够拿到 编译时注解 的引用 , 注解处理器 Module 需要依赖 编译时注解 Module ;
在 注解处理器 Module 的 build.gradle 的 dependencies 依赖中添加 implementation project(path: ':annotation') 依赖 ;
plugins {
id 'java-library'
}
dependencies {
//需要依赖annotation模块,获取这个模块的注解
implementation project(path: ':annotation')
//使用google的auto-service依赖库
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc6'
implementation 'com.google.auto.service:auto-service:1.0-rc6'
}
java {
sourceCompatibility = JavaVersion.VERSION_1_7
targetCompatibility = JavaVersion.VERSION_1_7
}
八、设置 注解处理器 支持的注解类型
注解处理器 抽象类 AbstractProcessor 中的 getSupportedAnnotationTypes 方法 , 用于声明 注解处理器 要处理的注解类型 ;
该方法的返回值是 Set<String> , 因此可以设置多个处理的 注解类型 ;
在 getSupportedAnnotationTypes 方法中构造一个 Set<String> 集合 , 向其中放置要解析注解的全类名字符串 ;
/**
* 声明要处理的注解有哪些
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(OnClick.class.getCanonicalName());
return types;
}
设置 注解处理器 支持的注解类型 , 也可以使用 注解 的方式进行声明 ;
使用 @SupportedAnnotationTypes 注解 , 也可以声明 注解处理器 支持的注解类型 ;
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedAnnotationTypes {
/**
* Returns the names of the supported annotation types.
* @return the names of the supported annotation types
*/
String [] value();
}
注意 : 两种方式二选一 , 不能同时存在 ;
九、设置 注解处理器 支持的 Java 版本
注解处理器 抽象类 AbstractProcessor 中的 getSupportedSourceVersion 方法 , 用于声明 该注解处理器 支持的 Java 版本 ;
一般情况下要支持到最新的 Java 版本 ,
通过调用 ProcessingEnvironment 类的 getSourceVersion 方法 , 可以获取最新的 Java 版本 ;
/**
* 声明支持的java版本
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
设置 注解处理器 支持的 Java 语言版本 , 也可以使用 注解 的方式进行声明 ;
使用 @SupportedSourceVersion 注解 , 也可以声明 注解处理器 支持的 Java 语言版本 ;
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface SupportedSourceVersion {
/**
* Returns the latest supported source version.
* @return the latest supported source version
*/
SourceVersion value();
}
注意 : 两种方式二选一 , 不能同时存在 ;
十、获取被 注解 标注的节点
处理注解的核心逻辑在 AbstractProcessor 中的 process 方法中实现 ;
先获取被注解标注的节点 , 搜索 BindView , 调用 process 方法的 RoundEnvironment roundEnvironment 参数的 getElementsAnnotatedWith 方法 , 即可搜索到整个 Module 中所有使用了 BindView 注解的元素 ;
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
将 BindView 注解放在什么元素上 , 得到的就是相应类型的元素 , 根据 注解类型 获取 被该注解类型 标注的元素 , 元素可能是类 , 方法 , 字段 ;
在 app 模块中 , 只有 MainActivity 中的一个 属性字段 使用 BindView 注解 , 调用 roundEnv.getElementsAnnotatedWith(BindView.class) 方法获取的元素就是 MainActivity 中的所有标记@BindView的成员变量 ;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import com.aapl.annotation.BindView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@BindView(R.id.tv2)
TextView tv2;
@BindView(R.id.tv3)
TextView tv3;
@BindView(R.id.iv1)
ImageView iv1;
@BindView(R.id.iv2)
ImageView iv2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tv1.setText("bind view success");
}
}
假设在 若干 Activity 中 , 若干位置 , 使用了 BindView 注解 , 那么在获取的所有使用了 BindView 注解的字段 Set<? extends Element> elements 中装载了所有的使用了该注解的字段 , 这些字段来自不同的 Activity 中 ;
这就需要将 Set<? extends Element> elements 中的 字段 按照 Activity 上下文进行分组 , 以便生成代码 ;
这样每个 Activity 界面都对应若干个 Set<? extends Element> elements 中的元素 ;
十一、Element 注解节点类型
使用注解标注的 Element 节点类型 :
ExecutableElement : 使用注解的 方法 节点类型 ;
VariableElement : 使用注解的 字段 节点类型 , 类的成员变量 ;
TypeElement : 使用注解的 类 节点类型 ;
PackageElement : 使用注解的 包 节点类型 ;
上述 4个类都是 javax.lang.model.element.Element 的子类 ;
app module下完整源码参考如下:
ButterKnife.java代码:
import android.app.Activity;
import android.os.IBinder;
import java.lang.reflect.Constructor;
public class ButterKnife {
/**
* 获取到生成的XXXActivity_ViewBinder类文件
* @param obj
*/
public static void bind(Object obj) {
Class<?> clazz = obj.getClass();
String clazzName = clazz.getName()+"_ViewBinder";
try {
//获取到XXXActivity_ViewBinder的class,并创建class实例对象
Class<?> binderClazz = Class.forName(clazzName);
//确定一个类binderClass是不是继承(或实现自)IButterKnife
if (IButterKnife.class.isAssignableFrom(binderClazz)) {
IButterKnife butterKnife = (IButterKnife) binderClazz.newInstance();
butterKnife.bind(obj);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
IButterKnife.java代码:
public interface IButterKnife<T> {
void bind(T target);
}
AnnotationCompiler.java代码:
import com.aapl.annotation.BindView;
import com.aapl.annotation.OnClick;
import com.google.auto.service.AutoService;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.PackageElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.tools.Diagnostic;
import javax.tools.JavaFileObject;
/**
* 注解处理器(目的是生成一个java文件,生成一个窗体的findViewById和onClick的代码)
* 1.继承AbstractProcessor
* 2.注册服务,注册注解处理器
*/
@AutoService(Processor.class)
public class AnnotationCompiler extends AbstractProcessor {
//生成文件的对象
private Filer filer;
//日志打印
private Messager messager;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init(processingEnv);
filer = processingEnv.getFiler();
messager = processingEnv.getMessager();
}
/**
* 声明要处理的注解有哪些
* @return
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new HashSet<>();
types.add(BindView.class.getCanonicalName());
types.add(OnClick.class.getCanonicalName());
return types;
}
/**
* 声明支持的java版本
* @return
*/
@Override
public SourceVersion getSupportedSourceVersion() {
return processingEnv.getSourceVersion();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
// PackageElement 包节点
// TypeElement 类节点
// ExecutableElement 方法节点
// VariableElement 成员变量节点
/**
* 搜索 BindView , 将 BindView 注解放在什么元素上 , 得到的就是相应类型的元素
* 根据 注解类型 获取 被该注解类型 标注的元素 , 元素可能是类 , 方法 , 字段 ;
* 通过 getElementsAnnotatedWith 方法可以搜索到整个 Module 中所有使用了 BindView 注解的元素
*/
Set<? extends Element> elements = roundEnvironment.getElementsAnnotatedWith(BindView.class);
/**
* 定义一个map集合,用来存储所有activity中所有的变量注解集合
* key->activityName activity名字
* value->List<VariableElement> 成员变量注解集合
*/
Map<String, List<VariableElement>> map = new HashMap<>();
for (Element element : elements) {
//转化为成员变量节点
VariableElement variableElement = (VariableElement) element;
/**
* 先获取该注解节点的上一级节点 , 注解节点是 VariableElement 成员字段节点,
* 上一级节点是就是 Activity 类节点对应的 类节点 TypeElement
*/
TypeElement typeElement = (TypeElement) variableElement.getEnclosingElement();
String qualifiedName = typeElement.getQualifiedName().toString();
//获取到activityName = simpleName
String simpleName = typeElement.getSimpleName().toString();
System.out.println("qualifiedName="+qualifiedName+", simpleName="+simpleName);
messager.printMessage(Diagnostic.Kind.NOTE, "process==>qualifiedName="+qualifiedName+", simpleName="+simpleName);
//把当前activity中获取到的所有变量注解的节点都加入到list集合中
List<VariableElement> variableElements = map.get(simpleName);
if (variableElements == null) {
variableElements = new ArrayList<>();
map.put(simpleName, variableElements);
}
variableElements.add(variableElement);
}
if (!map.isEmpty()) {
Writer writer = null;
Iterator iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry<String, List<VariableElement>> entry = (Map.Entry<String, List<VariableElement>>) iterator.next();
String activityName = entry.getKey();
List<VariableElement> variableElements = entry.getValue();
//获取包名
String packageName = "";
TypeElement typeElement = null;
if (variableElements != null && !variableElements.isEmpty()) {
packageName = getPackageName(variableElements.get(0));
typeElement = (TypeElement) variableElements.get(0).getEnclosingElement();
}
// 生成一个java文件
String bindActivityName = activityName+"_ViewBinder";
try {
JavaFileObject javaFileObject = filer.createSourceFile(
packageName+"."+bindActivityName, typeElement);
writer = javaFileObject.openWriter();
StringBuffer sb = new StringBuffer();
sb.append("package "+packageName+";\n\n");
sb.append("import android.view.View;\n\n");
sb.append("public class "+bindActivityName+" implements IButterKnife<"+packageName+"."+activityName+"> {\n\n");
sb.append(" public void bind("+packageName+"." + activityName + " target) {\n");
for (VariableElement variableElement : variableElements) {
//获取到成员变量的名字
String filedName = variableElement.getSimpleName().toString();
//获取到注解中的值,即(R.id.xxx)
int resId = variableElement.getAnnotation(BindView.class).value();
sb.append(" target."+filedName+" = target.findViewById("+resId+");\n");
}
sb.append("");
sb.append(" }\n");
sb.append("}\n");
writer.write(sb.toString());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return false;
}
/**
* 获取包名
* @param typeElement
* @return
*/
public String getPackageName(Element typeElement) {
PackageElement packageElement = processingEnv.getElementUtils().getPackageOf(typeElement);
String qualifiedName = packageElement.getQualifiedName().toString();
return qualifiedName;
}
}
MainActivity.java代码:
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ImageView;
import android.widget.TextView;
import com.aapl.annotation.BindView;
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
TextView tv1;
@BindView(R.id.tv2)
TextView tv2;
@BindView(R.id.tv3)
TextView tv3;
@BindView(R.id.iv1)
ImageView iv1;
@BindView(R.id.iv2)
ImageView iv2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
tv1.setText("bind view success");
}
}
运行之后生成的MainActivity_ViewBinder.java
import android.view.View;
public class MainActivity_ViewBinder implements IButterKnife<com.aapl.butterknifedemo.MainActivity> {
public void bind(com.aapl.butterknifedemo.MainActivity target) {
target.tv1 = target.findViewById(2131231129);
target.tv2 = target.findViewById(2131231130);
target.tv3 = target.findViewById(2131231131);
target.iv1 = target.findViewById(2131230915);
target.iv2 = target.findViewById(2131230916);
}
}