2. URCap架构与生命周期
URCap 在 PolyScope 中并不是一个普通脚本或配置文件,而是一个遵循 OSGi 规范的 Java 模块。理解它的架构,核心是理解三件事:Bundle 如何组织、Activator 如何接管启动与停止、服务如何被注册和消费。只有把这三件事串起来,才能解释为什么一个 URCap 安装后能出现在示教器上,卸载后又能干净地消失。
一、OSGi Bundle 结构:URCap 的物理形态
一个 URCap 的安装包通常是 .urcap 文件,它本质上是一个 ZIP 归档,内部包含 META-INF/MANIFEST.MF、Java 类文件、资源文件以及可选的 pom.xml 或构建描述。PolyScope 在安装时会把 Bundle 放入 OSGi 框架的 Bundle 仓库,并解析其依赖。
MANIFEST.MF 是 Bundle 的“身份证”,其中几个关键头信息决定了 URCap 能否被正确加载:
Bundle-SymbolicName:Bundle 的唯一标识,例如com.example.gripper,同一框架内不可重复。Bundle-Version:版本号,如1.0.0,用于依赖解析和升级判断。Bundle-Activator:指定激活器类的全限定名,例如com.example.gripper.Activator。Import-Package:声明需要从其他 Bundle 导入的包,如com.ur.urcap.api。Export-Package:声明本 Bundle 对外暴露的包,供其他 Bundle 使用。
如果 Import-Package 中声明的包在框架内找不到对应导出,Bundle 会停留在 INSTALLED 状态而无法进入 RESOLVED,表现为 URCap 安装后不显示。这是实际开发中最常见的“装上了但没反应”的原因之一。
二、Activator:生命周期的入口
Activator 是 URCap 与 OSGi 框架之间的契约。它通常继承 BundleActivator 接口,实现两个方法:
public class Activator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
// 注册服务、创建 UI 贡献、初始化资源
}
@Override
public void stop(BundleContext context) throws Exception {
// 注销服务、释放资源、清理监听器
}
}
start() 在 Bundle 被启动时调用,此时应完成服务注册和 UI 扩展点的挂载;stop() 在 Bundle 停止或卸载前调用,必须把 start() 中申请的资源全部释放,否则会造成服务残留或内存泄漏。PolyScope 在关闭或卸载 URCap 时会触发 stop(),如果这里没有注销服务,下次安装同 SymbolicName 的 Bundle 可能因服务冲突而失败。
三、服务注册与消费:Bundle 之间的协作
URCap 的能力通常以 OSGi 服务的形式暴露。例如,一个夹爪 URCap 可能注册 GripperService,供其他 URCap 或 PolyScope 内部模块调用。注册方式如下:
context.registerService(
GripperService.class.getName(),
new GripperServiceImpl(),
null
);
消费方通过 ServiceTracker 或 context.getServiceReference() 获取服务。服务注册与注销必须成对出现,且应在 start()/stop() 中完成,而不是在构造函数或静态块中,否则生命周期不可控。
四、安装、启动、停止、卸载的完整流程
从 PolyScope 的角度看,一个 URCap 的生命周期经历以下阶段:
- 安装:用户通过示教器或 U 盘导入
.urcap文件,PolyScope 解压并安装 Bundle 到 OSGi 框架,状态变为INSTALLED。 - 解析:框架检查
Import-Package与Export-Package,依赖满足后进入RESOLVED。 - 启动:框架调用 Activator 的
start(),Bundle 进入ACTIVE,UI 贡献和服务开始生效。 - 停止:用户禁用或系统关闭时,框架调用
stop(),Bundle 回到RESOLVED。 - 卸载:Bundle 被从框架中移除,相关服务和 UI 贡献全部消失。
ACTIVE;如果停在 INSTALLED,多半是依赖包未解析;如果 start() 抛异常,框架会记录日志,可在 PolyScope 的日志中定位。五、生命周期状态与关键动作对照
下面的 SVG 把 Bundle 状态、Activator 调用和用户可见结果放在同一张图中,便于对照理解。
六、实操中的关键判断
在开发阶段,可以通过 PolyScope 的日志或 OSGi 控制台查看 Bundle 状态。若安装后 URCap 未出现,按以下顺序排查:
- 检查
Bundle-SymbolicName是否与已有 Bundle 冲突。 - 检查
Import-Package中的包是否都能在框架内找到导出方。 - 检查
Bundle-Activator类名是否拼写正确,且类在 Bundle 的 classpath 中。 - 检查
start()是否抛出异常,异常会导致 Bundle 无法进入ACTIVE。 - 检查
stop()是否遗漏注销服务,残留服务会阻止同 SymbolicName 的 Bundle 再次启动。
stop() 再移除 Bundle。如果 stop() 中抛出异常,卸载可能不完整,导致下次安装时出现“服务已存在”或“Bundle 无法解析”的错误。因此 stop() 中的清理逻辑必须健壮,建议用 try-catch 包裹每个注销动作。理解 URCap 的生命周期,本质上就是理解 OSGi 框架如何管理一个 Bundle 从安装到卸载的全过程。Activator 是开发者唯一能插入自定义逻辑的入口,服务注册与注销是 Bundle 之间协作的桥梁。把这两点做对,URCap 的安装、启动、停止和卸载就能稳定可控,后续的 UI 扩展和功能实现才有可靠的基础。