怎样写一个类似ROS的易用的android机器人框架(3)
我们实现了类似ROS系统的节点注册和消息转发机制,在这个机制中,节点自定义的消息封装在Bundle
对象中,这样每个节点的 publish()
都统一了,但是也有不好的,用起来就没那么方便,我们更希望定义自己的消息类,将一些复杂的数据填入这个类对象中,然后发布,如机器人马达的数据,可以定义:
data class MsgMotor(@JvmField var L: Int = 0, @JvmField var R: Int = 0)
转换成Bundle就得这么写
Bundle().apply {
putInt("L", xxx.L)
putInt("R",xxx.R)
}
其他节点接收到这个消息时又得反向解析一遍,很麻烦 能否简单解决呢?
利用 apt 自动生成转换类的代码
apt可以扫描源码中特定的注解,并根据注解生成特定的代码,因此我们开发apt工具库,然后在让编译脚本调用这个工具库帮我们生成这些转换代码。源码目录下
- easyAptApi : 包含源码和apt工具库需要识别的注解的定义
- easyAptProcessor: 是实现这个代码处理过程的实现
gradle脚本中指定 **kapt **(apt的Kotlin实现)processor的设置和依赖
dependencies {
compile project(":easyAptApi")
//指定 apt
kapt project(":easyAptProcessor")
}
需要转换的类添加注解
@MessageType("")
data class MsgMotor(@JvmField var L: Int = 0, @JvmField var R: Int = 0)
然后编译 kapt 自动在 generated 目录下生成转换类的源码
public final class ConvertMsgMotor {
public static Bundle toBundle(MsgMotor msgmotor) {
Bundle b = new Bundle();
try {
b.putInt("L", msgmotor.L);
b.putInt("R", msgmotor.R);
} catch (Exception e) {
e.printStackTrace();
}
return b;
}
public static MsgMotor toMsg(Bundle b) {
MsgMotor r = new MsgMotor();
try {
r.L = b.getInt("L");
r.R = b.getInt("R");
} catch (Exception e) {
e.printStackTrace();
}
return r;
}
}
使用这个ConvertMsgMotor即可实现MsgMotor与Bundle之间的相互转换了。
kapt processor 的实现过程
gradle脚本添加
dependencies {
compile project(':easyAptApi')
compile 'com.google.auto.service:auto-service:1.0-rc2'
compile 'com.squareup:javapoet:1.9.0'
}
定义主类
processor的主类需要实现 AbstractProcessor
接口,并添加注解 @AutoService(Processor::class)
,这是com.google.auto.service中的注解,用来生成apt插件库
需要定义 resource 目录下的 META-INF/services/javax.annotation.processing.Processor文件,填写主类的完整包名和类名,否则编译脚本将无法搜索到这个插件类
实现 AbstractProcessor
的所有方法
- override fun init(processingEnv: ProcessingEnvironment) :插件初始化时调用
- override fun getSupportedAnnotationTypes(): Set<String> : 必须返回所有需要处理的注解的完整包名类名的集合,否则将忽略不处理
- override fun process(annotations: Set<TypeElement>, roundEnv: RoundEnvironment): Boolean : 真正的处理函数,可在此调用 com.squareup:javapoet 库生成 java文件
通过Java Peot库生成java源代码文件
这样,一个根据注解自动生成代码的apt插件库机完成了