电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

如何使用Android注解處理器

瀏覽:4日期:2022-09-19 15:15:31

我們就可以結合今天的Annotation Processing Tool(APT)來自定義注解處理器。

注解處理器簡單解釋就是收集我們標記的注解,處理注解上提供的信息。

本篇用我之前寫的Saber舉例說明。

1.定義注解

推薦New -> Module -> Java Library,新建一個Java Library Module,命名為xx-annotation。用來單獨存放注解。

既然是注解處理器,那么首先需要有注解。自定義一個注解使用@interface關鍵字。

public @interface LiveData {}

然后我們需要用到注解的注解,也就是元注解來控制注解的行為。這里我簡單介紹一些元注解。

Retention 表示注解的保留范圍。值用RetentionPolicy枚舉類型表示,分為CLASS 、RUNTIME 、 SOURCE。 Target 表示注解的使用范圍。值用ElementType枚舉類型表示,有TYPE(作用于類)、FIELD(作用于屬性)、METHOD(作用于方法)等。

這里我的@LiveData注解作用是為了便于創建LiveData,而創建時需要知道數據類型。所以這個注解的使用范圍就是類和屬性。

其次這個注解處理生成模板代碼后,我們不需要保留在編譯后的.class文件中。所以可以使用SOURCE。

@Retention(RetentionPolicy.SOURCE)@Target({ElementType.FIELD, ElementType.TYPE})public @interface LiveData {}2.實現處理器

首先New -> Module -> Java Library,新建一個Java Library Module,命名為xx-complier。用來存放注解處理器。

如何使用Android注解處理器

創建一個繼承AbstractProcessor的類LiveDataProcessor。

public class LiveDataProcessor extends AbstractProcessor { @Override public SourceVersion getSupportedSourceVersion() {return SourceVersion.latestSupported(); } @Override public Set<String> getSupportedAnnotationTypes() {return Collections.singleton(LiveData.class.getCanonicalName()); } @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {return false; }}

需要實現三個方法,getSupportedSourceVersion 指定支持的Java版本,getSupportedAnnotationTypes指定處理的注解。process是處理注解的地方。

不過這里還需要初始化一些工具,可以重寫init 來實現。

private Elements elementUtils; // 操作元素的工具類private Filer filer; // 用來創建文件private Messager messager; // 用來輸出日志、錯誤或警告信息@Overridepublic synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); this.elementUtils = processingEnv.getElementUtils(); this.filer = processingEnv.getFiler(); this.messager = processingEnv.getMessager();}

下面就是重點process了,我們的注解作用范圍是類和屬性。所以我們需要將同一個類下的注解整理到一起。這里使用getElementsAnnotatedWith循環所有注解元素。

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for (Element element : roundEnv.getElementsAnnotatedWith(LiveData.class)) {if (element.getKind() == ElementKind.FIELD) { handlerField((VariableElement) element); // 表示一個字段}if (element.getKind() == ElementKind.CLASS) { handlerClass((TypeElement) element); // 表示一個類或接口}// ExecutableElement表示某個類或接口的方法 } return true;}private void handlerClass(TypeElement element) { ClassEntity classEntity = new ClassEntity(element); String className = element.getSimpleName().toString(); if (classEntityMap.get(className) == null) {classEntityMap.put(className, classEntity); }}private void handlerField(VariableElement element) { FieldEntity fieldEntity = new FieldEntity(element); String className = fieldEntity.getClassSimpleName(); if (classEntityMap.get(className) == null) {classEntityMap.put(className,new ClassEntity((TypeElement) element.getEnclosingElement())); } ClassEntity classEntity = classEntityMap.get(className); classEntity.addFieldEntity(fieldEntity);}

上面代碼中的element.getKind()獲取的是元素種類,對應的基本是上面元注解ElementType的類型。

ElementType ElementKind Element TYPE CLASS TypeElement FIELD FIELD VariableElement METHOD METHOD ExecutableElement

下面是封裝的簡易element,便于實際的使用。

class ClassEntity { private final TypeElement element; private final Name classSimpleName; private final Map<String, FieldEntity> fields = new HashMap<>(); public ClassEntity(TypeElement element) {this.element = element;this.classSimpleName = element.getSimpleName(); } public String getClassSimpleName() {return classSimpleName.toString(); } public void addFieldEntity(FieldEntity fieldEntity) {String fieldName = fieldEntity.getElement().toString();if (fields.get(fieldName) == null) { fields.put(fieldName, fieldEntity);} } public TypeElement getElement() {return element; } public Map<String, FieldEntity> getFields() {return fields; }}class FieldEntity { private VariableElement element; private String classSimpleName; public FieldEntity(VariableElement element) {this.element = element;this.classSimpleName = element.getEnclosingElement().getSimpleName().toString(); } public VariableElement getElement() {return element; } public String getClassSimpleName() {return classSimpleName; }}

下面就是使用JavaPoet來生成代碼,具體使用見JavaPoet使用攻略。這部分直接上代碼:

private JavaFile brewViewModel(Map.Entry<String, ClassEntity> item) { ClassEntity classEntity = item.getValue(); LiveData liveData = classEntity.getElement().getAnnotation(LiveData.class); /*類名*/ String className = classEntity.getElement().getSimpleName().toString() + 'ViewModel'; ClassName viewModelClazz = ClassName.get('androidx.lifecycle', 'ViewModel'); TypeSpec.Builder builder = TypeSpec .classBuilder(className) .addModifiers(Modifier.PUBLIC) .superclass(viewModelClazz); // 優先執行類LiveData注解 if (liveData != null){TypeName valueTypeName = ClassName.get(classEntity.getElement());brewLiveData(classEntity.getClassSimpleName(), valueTypeName, builder); }else {Map<String, FieldEntity> fields = classEntity.getFields();for (FieldEntity fieldEntity : fields.values()){ String fieldName = StringUtils.upperCase(fieldEntity.getElement().getSimpleName().toString()); TypeName valueTypeName = ClassName.get(fieldEntity.getElement().asType()); brewLiveData(fieldName, valueTypeName, builder);} } TypeSpec typeSpec = builder.build(); // 指定包名 return JavaFile.builder('com.zl.weilu.saber.viewmodel', typeSpec).build();}private void brewLiveData(String fieldName, TypeName valueTypeName, TypeSpec.Builder builder){ String liveDataType; ClassName liveDataTypeClassName; liveDataType = 'm$L = new MutableLiveData<>()'; liveDataTypeClassName = ClassName.get('androidx.lifecycle', 'MutableLiveData'); ParameterizedTypeName typeName = ParameterizedTypeName.get(liveDataTypeClassName, valueTypeName); FieldSpec field = FieldSpec.builder(typeName, 'm' + fieldName, Modifier.PRIVATE) .build(); MethodSpec getMethod = MethodSpec .methodBuilder('get' + fieldName) .addModifiers(Modifier.PUBLIC) .returns(field.type) .beginControlFlow('if (m$L == null)', fieldName) .addStatement(liveDataType, fieldName) .endControlFlow() .addStatement('return m$L', fieldName) .build(); MethodSpec getValue = MethodSpec .methodBuilder('get' + fieldName + 'Value') .addModifiers(Modifier.PUBLIC) .returns(valueTypeName) .addStatement('return this.$N().getValue()', getMethod) .build(); MethodSpec setMethod = MethodSpec .methodBuilder('set' + fieldName) .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(valueTypeName, 'mValue') .beginControlFlow('if (this.m$L == null)', fieldName) .addStatement('return') .endControlFlow() .addStatement('this.m$L.setValue(mValue)', fieldName) .build(); MethodSpec postMethod = MethodSpec .methodBuilder('post' + fieldName) .addModifiers(Modifier.PUBLIC) .returns(void.class) .addParameter(valueTypeName, 'mValue') .beginControlFlow('if (this.m$L == null)', fieldName) .addStatement('return') .endControlFlow() .addStatement('this.m$L.postValue(mValue)', fieldName) .build(); builder.addField(field) .addMethod(getMethod) .addMethod(getValue) .addMethod(setMethod) .addMethod(postMethod);}

輸出文件:

@Overridepublic boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { ... for (Map.Entry<String, ClassEntity> item : classEntityMap.entrySet()) {try { brewViewModel(item).writeTo(filer);} catch (Exception e) { messager.printMessage(Diagnostic.Kind.ERROR, e.getMessage(), item.getValue().getElement());} } return true;}3.注冊處理器

注冊處理器才可以使處理器生效,使用Google開源的AutoService的庫。

dependencies { implementation ’com.squareup:javapoet:1.13.0’ implementation ’com.google.auto.service:auto-service:1.0-rc7’ annotationProcessor ’com.google.auto.service:auto-service:1.0-rc7’}

然后添加@AutoService注解即可。

@AutoService(Processor.class)public class LiveDataProcessor extends AbstractProcessor { }4.調試注解處理器

項目的gradle.properties中配置:

org.gradle.daemon=trueorg.gradle.jvmargs=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005

接著Run -> Edit Configurations -> 點擊左上角加號 -> 選擇 Remote -> 指定module(可選)

如何使用Android注解處理器

注意兩個端口號一致。然后選擇添加的“APT”,點擊debug按鈕啟動。

后面就是打斷點,編譯項目即可。

5.支持增量編譯

Gradle 在 5.0 增加了對 Java 增量編譯的支持,通過增量編譯,我們能夠獲得一些優點:

更少的編譯耗時 更少的字節碼修改

如果注解處理器沒有支持增量編譯,那么編譯時,會輸出以下日志:

w: [kapt] Incremental annotation processing requested, but support is disabled because the following processors are not incremental: com.x.XXProcessor (NON_INCREMENTAL).

Gradle 支持兩種注解處理器的增量編譯:isolating 和 aggregating。

支持方法是在 META-INF/gradle/incremental.annotation.processors 文件中聲明支持增量編譯的注解處理器。

xx-complier/src/main/├── java│ ...│ └── LiveDataProcessor└── resources └── META-INF├── gradle│ └── incremental.annotation.processors└── services └── javax.annotation.processing.Processor

incremental.annotation.processors內容如下:

com.zl.weilu.saber.compiler.LiveDataProcessor,aggregating

這部分詳細內容見 讓 Annotation Processor 支持增量編譯。

6.使用處理器

添加依賴:

dependencies { implementation project(’:saber-annotation’) annotationProcessor project(’:saber-compiler’)}

首先創建一個類,使用@LiveData注解標記你要保存的數據。

public class SeekBar { @LiveData Integer value;}

Build ? > Make Project 生成代碼如下:

public class SeekBarViewModel extends ViewModel { private MutableLiveData<Integer> mValue; public MutableLiveData<Integer> getValue() { if (mValue == null) { mValue = new MutableLiveData<>(); } return mValue; } public Integer getValueValue() { return getValue().getValue(); } public void setValue(Integer mValue) { if (this.mValue == null) { return; } this.mValue.setValue(mValue); } public void postValue(Integer mValue) { if (this.mValue == null) { return; } this.mValue.postValue(mValue); }}

至此,我們就完成了一個簡單的自定義處理器。

以上就是如何使用Android注解處理器的詳細內容,更多關于Android注解處理器的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
主站蜘蛛池模板: 齿轮减速机电机一体机_齿轮减速箱加电机一体化-德国BOSERL蜗轮蜗杆减速机电机生产厂家 | 番茄畅听邀请码怎么输入 - Dianw8.com | 美缝剂_美缝剂厂家_美缝剂加盟-地老板高端瓷砖美缝剂 | 北京办公室装修,办公室设计,写字楼装修-北京金视觉装饰工程公司 北京成考网-北京成人高考网 | 微信聊天记录恢复_手机短信删除怎么恢复_通讯录恢复软件下载-快易数据恢复 | 杭州顺源过滤机械有限公司官网-压滤机_板框压滤机_厢式隔膜压滤机厂家 | 电缆桥架生产厂家_槽式/梯式_热镀锌线槽_广东东莞雷正电气 | 企业彩铃制作_移动、联通、电信集团彩铃上传开通_彩铃定制_商务彩铃管理平台-集团彩铃网 | 耐腐蚀泵,耐腐蚀真空泵,玻璃钢真空泵-淄博华舜耐腐蚀真空泵有限公司 | 济南办公室装修-厂房装修-商铺装修-工装公司-山东鲁工装饰设计 | 热风机_工业热风机生产厂家上海冠顶公司提供专业热风机图片价格实惠 | 二维运动混料机,加热型混料机,干粉混料机-南京腾阳干燥设备厂 | 中药二氧化硫测定仪,食品二氧化硫测定仪|俊腾百科 | 安平县鑫川金属丝网制品有限公司,防风抑尘网,单峰防风抑尘,不锈钢防风抑尘网,铝板防风抑尘网,镀铝锌防风抑尘网 | 希望影视-高清影视vip热播电影电视剧免费在线抢先看 | 手术室净化装修-手术室净化工程公司-华锐手术室净化厂家 | 广东恩亿梯电源有限公司【官网】_UPS不间断电源|EPS应急电源|模块化机房|电动汽车充电桩_UPS电源厂家(恩亿梯UPS电源,UPS不间断电源,不间断电源UPS) | 西安耀程造价培训机构_工程预算实训_广联达实作实操培训 | 标策网-专注公司商业知识服务、助力企业发展 | ASA膜,ASA共挤料,篷布色母料-青岛未来化学有限公司 | 运动木地板价格,篮球馆体育运动木地板生产厂家_欧氏地板 | 纸布|钩编布|钩针布|纸草布-莱州佳源工艺纸布厂 | 健身器材-健身器材厂家专卖-上海七诚健身器材有限公司 | 东莞喷砂机-喷砂机-喷砂机配件-喷砂器材-喷砂加工-东莞市协帆喷砂机械设备有限公司 | 气象监测系统_气象传感器_微型气象仪_气象环境监测仪-山东风途物联网 | 贴片电容-贴片电阻-二三极管-国巨|三星|风华贴片电容代理商-深圳伟哲电子 | 首页-瓜尔胶系列-化工单体系列-油田压裂助剂-瓜尔胶厂家-山东广浦生物科技有限公司 | 物联网卡_物联网卡购买平台_移动物联网卡办理_移动联通电信流量卡通信模组采购平台? | 面粉仓_储酒罐_不锈钢储酒罐厂家-泰安鑫佳机械制造有限公司 | 宠物店加盟_宠物连锁店_开宠物店-【派多格宠物】 | 环境模拟实验室_液体-气体控温机_气体控温箱_无锡双润冷却科技有限公司 | 葡萄酒灌装机-食用油灌装机-液体肥灌装设备厂家_青州惠联灌装机械 | 拉伸膜,PE缠绕膜,打包带,封箱胶带,包装膜厂家-东莞宏展包装 | 宽带办理,电信宽带,移动宽带,联通宽带,电信宽带办理,移动宽带办理,联通宽带办理 | 泡沫消防车_水罐消防车_湖北江南专用特种汽车有限公司 | 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 - 杭州标识标牌|文化墙|展厅|导视|户内外广告|发光字|灯箱|铭阳制作公司 | 成都思迪机电技术研究所-四川成都思迪编码器 | 防爆电机生产厂家,YBK3电动机,YBX3系列防爆电机,YBX4节防爆电机--河南省南洋防爆电机有限公司 | 自动焊锡机_点胶机_螺丝机-锐驰机器人| 双菱电缆-广州电缆厂_广州电缆厂有限公司| 【灵硕展览集团】展台展会设计_展览会展台搭建_展览展示设计一站式服务公司 |