Android(安卓)利用Annotation Processing 编译时通过注解自动生成代码
16lz
2022-07-31
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/servicesimplementation '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 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!!!"); }
更多相关文章
- 浅谈Java中Collections.sort对List排序的两种方法
- Python list sort方法的具体使用
- python list.sort()根据多个关键字排序的方法实现
- android上一些方法的区别和用法的注意事项
- android实现字体闪烁动画的方法
- Android中dispatchDraw分析
- Android四大基本组件介绍与生命周期
- Android(安卓)MediaPlayer 常用方法介绍
- 在Fragment中设置控件点击方法,执行失败。