栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

APT与ButterKnife生成代码,AbstractProcessor实现

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

APT与ButterKnife生成代码,AbstractProcessor实现

首先我们来了解apt的定义;它是干什么用的?和Java AbstractProcessor来实现ButterKnife的自定义。

APT篇:

Annotation processing Tool简称: APT即注解处理器,它是一种处理注解的工具,也是javac中的一个工具。APT可以用来在编译时扫描和处理注解。 这类API的使用被广泛的用于各种框架,如dubbo,lombok等。

APT作用:

获取到注解和被注解对象的相关信息可以通过APT 实现;然后拿到这些信息后我们可以根据需求来自动的生成一些代码,就省去了手动编写。举例:如 ButterKnife、Dagger 等三方框架在Android中,都是采用APT。

两种处理方式:

Spring和Spring Boot : 最常见也是最显式化的就是Spring以及Spring Boot的注解实现了,在运行期容器启动时,根据注解扫描类,并加载到Spring容器中。而另一种就是本文主要介绍的注解处理,即编译期注解处理器,用于在编译期通过JDK提供的API,对Java文件编译前生成的Java语法树进行处理,实现想要的功能。

使用注解处理器:

第一步我们来创建java库,在库中定义自己的注解,如:

@Documented

@Target( {ElementType.FIELD})

@Retention(value = RetentionPolicy . RUNTIME )

public @interface TestSelfAnnotation {

int value() default 0;

}

第二步 创建java库后,添加如下依赖,编写自己的注解处理器继承 AbstractProcessor ,生成代码使用 JavaPoet 库,生成.java源文件。

@AutoService(Processor.class) public class MyAnnotationProcessor extends AbstractProcessor {

private Map getOptions;

private Messager getMessagers;

private Filer getFilers;

private Elements elementUtils;

private Types typeUtils;

private SourceVersion getSourceVersion;
private Locale getLocale;
private static final String RANDOM_SUFFIX = “$$BindView”;
private HashMap> entryKey = new HashMap<>();
private HashMap elementIndexMap = new HashMap<>();
@Override
public boolean process(Set set, RoundEnvironment roundEnvironment) {

Set elementsAnnotatedWith = roundEnvironment.getElementsAnnotatedWith(TestSelfAnnotation.class);
for (final Element element : elementsAnnotatedWith)
    if (element.getKind() == ElementKind.FIELD) {

        TestSelfAnnotation annotation = element.getAnnotation(TestSelfAnnotation.class);
        TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
        ClassName className = ClassName.get(enclosingElement);

        if (entryKey.containsKey(className.simpleName())) {
            HashMap stringStringHashMap = entryKey.get(className.simpleName());
            stringStringHashMap.put(element.getSimpleName().toString(), annotation.value() + "");

// entryKey.put(element.getEnclosingElement().getSimpleName().toString(), stringStringHashMap);

} else {

HashMap hashMap = new HashMap<>();

hashMap.put(element.getSimpleName().toString(),

annotation.value() + “”);

elementIndexMap.put(className.simpleName(),

element);

entryKey.put(className.simpleName(), hashMap);

} }

writeClazz(entryKey, elementIndexMap);

return false;

}

private void writeClazz(HashMap> entryKey, HashMap elementIndexMap) {

for (Map.Entry> map :

entryKey.entrySet()) {

String key = map.getKey();

HashMap value = map.getValue();

Element element = elementIndexMap.get(key);

TypeElement enclosingElement = (TypeElement)

element.getEnclosingElement();

ClassName className = ClassName.get(enclosingElement);

MethodSpec.Builder builder =

MethodSpec.constructorBuilder()

.addModifiers(Modifier.PUBLIC)

.addParameter(className, “activity”);

if (value.size() > 0) {

for (HashMap.Entry entry : value.entrySet()) {

CodeBlock of = CodeBlock.of(“$N.” + entry.getKey() + “=” +

“$N.” + “findViewById(”

entry.getValue() + “);n”, “activity”, “activity”);

}

}

TypeSpec build =

TypeSpec.classBuilder(getClazzName(element) + RANDOM_SUFFIX)

.addModifiers(Modifier.PUBLIC)

.addMethod(builder.build())

.build();

try {

JavaFile javaFile =

JavaFile.builder(getPackageName(element), build).build();

javaFile.writeTo(getFilers); } catch (IOException e) {

e.printStackTrace();

}

}

}

private String getClazzName(Element element) {
Element enclosingElement = element.getEnclosingElement();
return enclosingElement.getSimpleName().toString();
}
private String getPackageName(Element element) {
PackageElement packageOf = elementUtils.getPackageOf(element);
return packageOf.getQualifiedName().toString();
}
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
getMessagers = processingEnvironment.getMessager();
getFilers = processingEnvironment.getFiler();
elementUtils = processingEnvironment.getElementUtils();
typeUtils = processingEnvironment.getTypeUtils();
}
@Override
public Set getSupportedAnnotationTypes() {
Set setClazzName = new LinkedHashSet<>();
System.out.println(" " + TestSelfAnnotation.class.getCanonicalName());
setClazzName.add(TestSelfAnnotation.class.getName());
return setClazzName;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.RELEASE_7;
}

自动生成如下文件:

文件:javax.annotation.processing. Processor。

第三步module依赖注解后,在apt文件下生成。

常见的几个API:

AbstractProcessor篇:

AbstractProcessor,是一个抽象类,该类实现了接口Processor。 抽象类AbstractProcessor以及接口Processor都是位于包 javax.annotation.processing中。该接口中定义的所有类、接口都是与实现注解处理器相关的。但JavaWeb开发中也很常用只是太过于底层,常用的lombok的实现逻辑就是基于注解处理器,SPI(服务提供接口)原理也可以使用注解处理器实现。

ButterKnife

原理:在编译过程中,获取到注解的内容,生成JAVA文件及代码,再通过反射机制调用JAVA文件里的方法。

简单来说就是:我自动帮你把findViewByld写了类似这样:

package com.apt.viewlinkiddemo;

Cclass MainActivity ViewLink {

public MainActivity Viewl ink(MainActivity activity) {

activity .textview = activily findViewByld(2131231 103);

activity .bt = activity findViewByld(2131230807);

AbstractProcessor类中各方法的用法:

一.Iterable

getCoapletioos (Elenent element, AnnotatiooMirror annotation, ExecutableElement meniber, string userText)返回一个空的completion迭代。

二.Set:

1.getSupportedAnnotationTypes( )

如果processor类是使用SupportedAnnotationTypes注释的,则返回一个不可修改的集合,该集合具有与注释相同的字符串集。

2.getSupportedOptions ( )

如果processor类是使用SupportedOptions注释的,则返回-一个不可修改的集合,该集合具有与注释相同的字符串集。

三.SourceVersion

getSupportedsourceversion( ) 如果processor类是使用SupportedSourceversion 注释的,则返回注释中的原版本。

四.void

init (ProcessingEnvironment process ingEnv )

用处理环境初始化processor,方法是将processingEnv字段设置为processingEnv参数的值。

五.protected boolean

isInitialized( ) 如果此对象已被初始化,则返回true,否则返回false。

六.abstract boolean

process(Set annotations, RoundEnvironment > annotations, RoundEnvironment roundEnv )

处理先前round产生的类型元素上的注释类型集,并返回这些注释是否由此Processor 声明。

ButterKnife自定义

在Android中,ButterKnife是一个通过注解实现依赖注入的框架,所以这里也通过实现一个简单的ButterKnife框架,熟悉通过 AbstractProcessor实现注解处理器的过程。

首先,需要创建两个Java库butterknife-annotations和butterknife-compiler以及一个android库butterknife

butterknife-annotations,用于声明具体的注解的,如BindView、OnClick等)。主要供butterknife-compiler和butterknife库引用 butterknife-compiler,用于声明自定义AbstractProcessor,在编译时生成具体的Java文件。 butterknife,这个就是具体android项目中需要引用的库,主要实现具体依赖注入的功能。 butterknife是一个android库工程,该库的作用主要就是完成依赖注入。在该库中会依赖butterknife-annotations,最终在Android项目中我们只需要依赖这个工程即可。

butterknife中定义的用于实现依赖注入的工具类如下:

public class ButterKnife {

public static void bind(Activity activity) {

//1、获得全限定类名次 String name = activity. getClass() . getName(); try { //2、柜据 全限定类名获职遇过注解解释器生成的Java类 Class clazz = Class. forName(name +”ViewBinding");

//3、遇过反射获 构造万法并创建实例完成依赖注入 clazz. getConstructor(activity . getClass()) . newInstance(activity); } catch (Exception e) { e. printStackTrace();

使用butterknife完成依赖注入

实例;需要在项目中也就是在app/build.gradle文件中添加库依赖并且声明APT工具,如下图:

测试:

在项目主工程中定义了MainActivity和TwoActivity,在这两个Activity中使用注解@BindView,如下图:

通过Android Studio的Rebuild Project,就可以看到在主app工程build/generated/source/apt/debug目录下就已经生成了两个新的Java文件 如图:

看看MainActivity_ViewBinding类中的具体内容,发现在该文件中为MainActivity_ViewBinding创建了一个构造方法,该构造方法中需要传入指定的activity,然后在该构造方法中就可以完成button和textView的初始化即findViewByid的过程。

1// Generated code from Butter Knife. Do not modify! 2.package com. . zhangke . simplifybutterknife;

4import android.widget . Button; 5import android .widget .TextView; 6public final class MainActivity. ViewBinding { 7public MainActivity. ViewBinding(MainActivity target) { 8target button . (Button) target findVi6eyId(231165219); 9target.textView . (TextView) trget.finviByI(213165305);

想完成依赖注入,只需要通过工具类ButterKnife的bind方法就可以了,这个在上面MainActivity的源码中可以看到。 其实以上整个流程就是开源项目ButterKnife的基本工作原理 。

到这里, 一个简单的自定义ButterKnife就完成了 。

总结一下,以上就是有关apt的一些技术点。关于更多Android开发技术点击:

https://shimo.im/docs/5rk9dyvKa4Fz1Kqx/ ,免费领取《免费Android技术丶面试题纲丶核心笔记资料》

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/831426.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号