Annotation Processor
annotationProcessor和android-apt的功能是一样的,它们是替代关系。annotationProcessor是APT工具中的一种,他是谷歌开发的内置框架,不需要引入,可以直接在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
-
-
-
public BindView {
-
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
-
-
public class MyProcessor extends AbstractProcessor {
-
-
-
}
备注:注解处理器创建完毕后,需要创建注册目录,如果是手动创建,目录如下:
文件里面内容为自己处理器的全路径path
建议使用谷歌的 AutoService,通过@AutoService注解标记一下,就可以自动完成注册动作,避免了手动创建错误,引用如下:
-
dependencies {
-
//谷歌的 AutoService
-
implementation 'com.谷歌.auto.service:auto-service:1.0-rc2'
-
annotationProcessor 'com.谷歌.auto.service:auto-service:1.0-rc2'
-
//自己的注解库
-
implementation project(':annotation')
-
}
生成路径:
Processor
常用到的4个方法:
-
/**
-
* 初始化常用的工具类
-
* @param processingEnv
-
*/
-
-
public synchronized void init(ProcessingEnvironment processingEnv) {
-
super.init(processingEnv);
-
filer = processingEnv.getFiler();//文件操作相关
-
messager = processingEnv.getMessager();//日志相关
-
elementUtils = processingEnv.getElementUtils();//元素相关
-
}
-
/**
-
* 支持的注解类型
-
* 也可以通过系统注解
-
* @SupportedAnnotationTypes({"com.example.annotation.BindView"})
-
* @return
-
*/
-
-
public Set<String> getSupportedAnnotationTypes() {
-
Set<String> types = new LinkedHashSet<>();
-
types.add(BindView.class.getCanonicalName());
-
return types;
-
}
-
/**
-
* 核心逻辑处理
-
* @param annotations
-
* @param roundEnv
-
* @return
-
*/
-
-
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment
-
roundEnv) {
-
return true;
-
}
-
/**
-
* 支持的java版本
-
* 也可以通过系统注解代替
-
* @return
-
*/
-
-
public SourceVersion getSupportedSourceVersion() {
-
return SourceVersion.RELEASE_7;
-
}
系统注解使用如下:
-
-
-
-
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使用,可以参考,感谢感谢,写的挺好的:
代码生成如下:
-
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 {
-
-
//关键
-
R.id.tv01)(
-
TextView tv01;
-
-
-
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讲解 - 帅气的码农 - 博客园
秒懂Android注解处理器(Android Annotation Processor)-蒲公英云
通过编译期生成代码方式实现的仿ButterKnife功能Demo
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhgfgcig
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
excel下划线不显示怎么办
PHP中文网 06-23 -
excel打印预览压线压字怎么办
PHP中文网 06-22 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
怎样阻止微信小程序自动打开
PHP中文网 06-13