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的 AutoServiceimplementation '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*/@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) {super.init(processingEnv);filer = processingEnv.getFiler();//文件操作相关messager = processingEnv.getMessager();//日志相关elementUtils = processingEnv.getElementUtils();//元素相关}
/*** 支持的注解类型* 也可以通过系统注解* @SupportedAnnotationTypes({"com.example.annotation.BindView"})* @return*/@Overridepublic Set<String> getSupportedAnnotationTypes() {Set<String> types = new LinkedHashSet<>();types.add(BindView.class.getCanonicalName());return types;}
/*** 核心逻辑处理* @param annotations* @param roundEnv* @return*/@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {return true;}
/*** 支持的java版本* 也可以通过系统注解代替* @return*/@Overridepublic 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 21targetSdkVersion 25versionCode 1versionName "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");//生成方法 1builder.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");//生成方法 2builder.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专栏的博客-CSDN博客_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;@Overrideprotected 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()的不同_正在飞翔的猫的博客-CSDN博客_getcanonicalname
implementation、api、compileOnly区别详解_XeonYu的博客-CSDN博客_compileonly implementation
深入理解编译注解(二)annotationProcessor与android-apt_珠穆朗玛小王子的博客-CSDN博客
注解与APT注解处理器技术详解 - 简书
注解入坑笔记:关于注解使用必须了解的——Annotation、AbstraceProcessor、APT_Else_Q的博客-CSDN博客
秒懂Android注解处理器(Android Annotation Processor)-蒲公英云
AbstractProcessor注解处理器_廖子尧的博客-CSDN博客_abstractprocessor
通过编译期生成代码方式实现的仿ButterKnife功能Demo