导航
一、代码的演进
二、butterKnife反射调用
三、javapoet自动生成模板代码
四、apt与注解
五、注解支持多层继承
六、apt调试
七、javapoet语法
1、前言
Annotation Processing Tool (apt)是编译期处理注解的工具
注解有三种【SOURCE,RUNTIME, CLASS】源码,运行时,编译,其中编译期效率最高的。
解决问题:通过注解,动态生成模板代码
apt与javapoet联动
1、抽象模板关键参数(只关注参数)
2、定义注解传入关键参数
3、引入apt
- 1、抽象模板参数
/**
* DO NOT EDIT THIS FILE!!! IT WAS GENERATED BY CHENTONG.
*/
public class FirstActivity_ViewBinding implements Unbinder {
private FirstActivity target;
@UiThread
public FirstActivity_ViewBinding(FirstActivity target, View source) {
this.target = target;
target.helloTv = source.findViewById( R.id.helloTv );
}
@Override
public void unbind() {
target.helloTv = null;
}
}
这个类中核心的一句话
target.helloTv = source.findViewById( R.id.helloTv );
因为获得注解的同时,就会获得当前元素(Type,Field,Method),所以helloTv,这个field不用关注
关键参数就是元素ResId。
- 2 定义注解
eg: @ViewId(R.id.helloTv)
1、新建module 选择选择java library ,工程名poet-annotation
2、新建注解 ViewId
@Retention(CLASS) @Target(FIELD)
public @interface ViewId {
/** View ID to which the field will be bound. */
@IdRes int value();
}
- 3、apt
新建module 命名为poet-compiler
其中编写的时候,要注意包
javax.annotation.processing
javax.lang.model
方法用法
base类 方便后续开发
public abstract class BaseProcessor extends AbstractProcessor {
protected Filer mFiler; //输出位置
protected Logger logger;
protected Types types;
protected Elements elementUtils;
// Module name, maybe its 'app' or others
protected String moduleName = null;
protected Map options = null;
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init( processingEnv );
mFiler = processingEnv.getFiler();
types = processingEnv.getTypeUtils();
elementUtils = processingEnv.getElementUtils();
logger = new Logger( processingEnv.getMessager() );
options = processingEnv.getOptions();
if (MapUtils.isNotEmpty( options )) {
moduleName = options.get( KEY_MODULE_NAME );
}
if (StringUtils.isNotEmpty( moduleName )) {
logger.info( "The user has configuration the module name, it was [" + moduleName + "]" );
} else {
logger.error( NO_MODULE_NAME_TIPS );
}
}
//判断当前是不是activity类
public boolean isActivity(TypeElement typeElement){
TypeMirror activityTm = elementUtils.getTypeElement(Consts.ACTIVITY).asType();
if (types.isSubtype(typeElement.asType(),activityTm )) return true;
return false;
}
//判断当前类是fragment
public boolean isFragment(TypeElement typeElement){
TypeMirror fragmentTm = elementUtils.getTypeElement(Consts.FRAGMENT).asType();
TypeMirror fragmentTmV4 = elementUtils.getTypeElement(Consts.FRAGMENT_V4).asType();
if (types.isSubtype(typeElement.asType(),fragmentTm )
|| types.isSubtype(typeElement.asType(),fragmentTmV4 )){
return true;
}
return false;
}
@Override
public Set getSupportedOptions() {
return new HashSet() {{
this.add( KEY_MODULE_NAME );
}};
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
注解生成代码类
@AutoService(Processor.class)
public class ViewIdProcessor extends BaseProcessor {
private Map> parentAndChild = new HashMap<>(); //包含父类的注解
@Override
public synchronized void init(ProcessingEnvironment processingEnv) {
super.init( processingEnv );
logger.info(">>> ViewIdProcessor init. <<<");
}
@Override
public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) {
if (CollectionUtils.isNotEmpty( annotations )){
Set extends Element> viewIdElements = roundEnv.getElementsAnnotatedWith(ViewId.class);
try {
categories( viewIdElements );
//支持多层注解 field字段去重 不需要注释掉即可
supportSuperAnnotation();
gennerateHelper();
} catch (Exception e) {
logger.error( e );
}
return true;
}
return false;
}
//自动生成代码
private void gennerateHelper() throws IOException {
//根据类路径获得类型
TypeElement type_unbinder = elementUtils.getTypeElement(Consts.UNBINDER);
//用于判断当前类类型
TypeMirror viewTm = elementUtils.getTypeElement( Consts.VIEW ).asType();
TypeElement uiThreadType = elementUtils.getTypeElement( Consts.UI_THREAD );
if (MapUtils.isNotEmpty( parentAndChild )){
for (Map.Entry> entry : parentAndChild.entrySet()){
TypeElement typeElement = entry.getKey();
List elementList = entry.getValue();
//类全路径
String qualifiedName = typeElement.getQualifiedName().toString();
//包名
String packageName = qualifiedName.substring(0, qualifiedName.lastIndexOf("."));
//类文件
String fileName = typeElement.getSimpleName() + SUFFIX;
//新建类Target_ViewBinding
TypeSpec.Builder targetClassType = TypeSpec.classBuilder( fileName )
.addModifiers( Modifier.PUBLIC)
.addJavadoc( WARNING_TIPS )
.addSuperinterface( ClassName.get(type_unbinder.asType()) ); //实现接口
//新建field target字段
FieldSpec targetField = FieldSpec.builder(TypeName.get( typeElement.asType() ),"target",Modifier.PRIVATE)
.build();
//target类增加一行field
targetClassType.addField( targetField );
//新建构造方法Target_ViewBinding(Target target,View source)
MethodSpec.Builder constructorBuilder = MethodSpec.constructorBuilder()
.addAnnotation( ClassName.get( uiThreadType ) )
.addModifiers( Modifier.PUBLIC )
.addParameter( TypeName.get( typeElement.asType() ),"target" )
.addParameter( TypeName.get( viewTm ),"source" )
.addStatement( "this.target = target" );
for (Element element :elementList){
//获取控件ID
ViewId viewIdAnnotation = element.getAnnotation( ViewId.class );
int viewId = viewIdAnnotation.value();
//获取当前field字段
String fieldName = element.getSimpleName().toString();
constructorBuilder.addStatement( "target."+fieldName + " = source.findViewById( $L )" ,viewId);
}
//创建构造方法
MethodSpec constructor = constructorBuilder.build();
//target类增加构造方法
targetClassType.addMethod( constructor );
//新建方法 unbind
MethodSpec.Builder unbindBuilder = MethodSpec.methodBuilder("unbind")
.addAnnotation( Override.class )
.addModifiers( Modifier.PUBLIC )
.returns( void.class );
for (Element element :elementList){
String fieldName = element.getSimpleName().toString();
unbindBuilder.addStatement( "target."+fieldName+" = null" );
}
//创建释放方法
MethodSpec unbinder = unbindBuilder.build();
//target类增加释放方法
targetClassType.addMethod( unbinder );
//创建target类
TypeSpec targetType = targetClassType.build();
//写类
JavaFile.builder(packageName, targetType).build().writeTo(mFiler);
//打印
JavaFile.builder(packageName, targetType).build().writeTo(System.out);
}
}
}
/**
*
* @param elements
* @throws IllegalAccessException
* 当前类type元素和当前类元素的列表
*/
private void categories(Set extends Element> elements) throws IllegalAccessException {
if (CollectionUtils.isNotEmpty(elements)) {
for (Element element : elements) {
//获得当前元素的TypeElement
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
if (element.getModifiers().contains(Modifier.PRIVATE)) {
throw new IllegalAccessException("The inject fields CAN NOT BE 'private'!!! please check field ["
+ element.getSimpleName() + "] in class [" + enclosingElement.getQualifiedName() + "]");
}
if (parentAndChild.containsKey(enclosingElement)) { // Has categries
parentAndChild.get(enclosingElement).add(element);
} else {
List childs = new ArrayList<>();
childs.add(element);
parentAndChild.put(enclosingElement, childs);
}
}
logger.info("categories finished.");
}
}
/**
* Field元素 注解支持多层继承
* 未做算法优化,仅做测试
*/
private void supportSuperAnnotation(){
TreeUtils tree = new TreeUtils( );
parentAndChild = tree.supportSuperAnnotation( parentAndChild );
}
//建议这种写法,减少改字符串
@Override
public Set getSupportedAnnotationTypes() {
return new HashSet() {{
this.add( ViewId.class.getName() );
}};
}
}
从无到有手写butterKnife框架
https://github.com/yinlingchaoliu/JavaPoetDemo
具体参考
https://cloud.tencent.com/developer/article/1006210