Android 利用Annotation Processing 编译时通过注解自动生成代码
类似ButterKnife的框架,因为时间有限只实现绑定View.主要是利用 Annotation Processing,编译的过程中读取注解,然后使用JavePoet生成我们想要的代码
项目结构
lib是一个Android Library,lib_annotation,lib_processor只需要是Java Library
依赖关系
左侧的的依赖是可以传递的
app
implementation project(":lib")
annotationProcessor project(":lib_processor")
lib
api project(":lib_annotation") //因为依赖要传递到app中,所以要用api
lib_processor
//自动生成代码的库
implementation 'com.squareup:javapoet:1.11.1'
//自动生成service,省去了配置resources/META-INF/services,配合@AutoService(Processor.class)使用
implementation 'com.google.auto.service:auto-service:1.0-rc4'
implementation project(':lib_annotation')
我们需要自动生成代码的样式
我们要自动生成这样的代码,代替我们findViewById的繁琐操作
public class MainActivity$Binding {
public MainActivity$Binding(MainActivity activity) {
activity.mTextView = activity.findViewById(2131165319);
}
}
实现BindProcessor
- 首先要继承AbstractProcessor
- 在java同级目录下创建resources/META-INF/services/javax.annotation.processing.Processor文件
或者com.lixxy.lib_processor.BindProcessor
//自动生成service,省去了配置resources/META-INF/services implementation 'com.google.auto.service:auto-service:1.0-rc4' //还需要在BindProcessor加注解@AutoService(Processor.class) @AutoService(Processor.class) public class BindProcessor extends AbstractProcessor { }
BindProcessor类
@AutoService(Processor.class)
public class BindProcessor extends AbstractProcessor {
//获取操作文件的工具
private Filer mFiler;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
mFiler = processingEnvironment.getFiler();
}
/**
* @param set 是 getSupportedAnnotationTypes返回的set
* @param roundEnvironment 运行注解所在的环境
* @return
*/
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//运行注解所在的环境class 此处就是testActivity
Set<? extends Element> rootElements = roundEnvironment.getRootElements();
for (Element rootElement : rootElements) {
//运行注解所在的环境.java所在的包
String packageStr = rootElement.getEnclosingElement().toString();
String classStr = rootElement.getSimpleName().toString();
ClassName className = ClassName.get(packageStr, classStr + "$Binding");
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
.addModifiers(Modifier.PUBLIC)
.addParameter(ClassName.get(packageStr, classStr), "activity");
boolean isBind = false;
//运行注解所在的环境.java的成员 包括成员字段Field,方法,内部类等
List<? extends Element> enclosedElements = rootElement.getEnclosedElements();
for (Element enclosedElement : enclosedElements) {
//如果是字段
if (enclosedElement.getKind().isField()) {
BindView bindView = enclosedElement.getAnnotation(BindView.class);
//如果是被BindView注解的字段
if (bindView != null) {
isBind = true;
//增加构造方法的内容
// $N是名称替换 $L 字面量替换 也就是直接替换,具体的看Javapoet的使用方法
constructorBuilder.addStatement("activity.$N = activity.findViewById($L)", enclosedElement.getSimpleName(), bindView.value());
}
}
}
TypeSpec typeSpec = TypeSpec.classBuilder(className)
.addModifiers(Modifier.PUBLIC)
.addMethod(constructorBuilder.build())
.build();
if (isBind) {
try {
JavaFile.builder(packageStr, typeSpec)
.build().writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
}
}
return false;
}
/**
* 这个注解处理器是注册给哪个注解的
* 注意,它的返回值是一个字符串的集合,包含本处理器想要处理的注解类型的合法全称。
* 换句话说,你在这里定义你的注解处理器注册到哪些注解上
*/
@Override
public Set<String> getSupportedAnnotationTypes() {
super.getSupportedAnnotationTypes();
return Collections.singleton(BindView.class.getCanonicalName());
}
}
实现注解
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.SOURCE) //只在编译期生效
@Target(ElementType.FIELD) //只对字段生效
public @interface BindView {
int value();
}
实现Bind类
public class Bind {
publicstatic void bind(Activity activity) {
//在这里对activity中的字段进行负值或者调用负值的方法
//使用反射的方法调用我们自动生成的类
try {
//获取class对象
Class<?> bindClass = Class.forName(activity.getClass().getCanonicalName() + "$Binding");
// 获取有参数的构造方法(构造方法的参数)
Constructor<?> constructor = bindClass.getDeclaredConstructor(Class.forName(activity.getClass().getCanonicalName()));
//调用构造方式
constructor.newInstance(activity);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
项目中使用
@BindView(R.id.textView)
TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Bind.bind(this);
mTextView.setText("Bind Success!!!");
}