# 插件生命周期
每个插件都有自己的生命周期, 你应该时刻关注
# 插件 - 积木块 - 角色 - 硬件设备 的关系
- 每个插件只会实例化一次
- 插件实例化的时候, 会实例化一个 BlockExtension
- BlockExtension 实例化的时候, 会调用
getInfo()
注册积木块以及对应的callback
(opcode 或 func 对应的同名函数) - 每次添加硬件角色 (就是同一类角色) , 会实例化一个角色对象, 会挂载 Toolbox 和 积木块 (只是挂载在角色对象上)
- 一旦角色进行连接操作, 就会实例化 硬件设备, 硬件角色挂载在角色上
可以看下图的对应关系
# 检索
当用户准备添加硬件的时候, 我们会检索对应的 manifest.json
, 这个时候我们只是获取里面的数据, 用来做展示, 告知用户, 插件的名字, 版本, 图标, 以及支持什么特性等等。
# 加载
当用户首次添加硬件的时候, 我们会加载插件代码
uCode 插件加载原理
前面有提到过 uCode v4 使用了一种沙盒系统,iframe Sandbox (opens new window),因此每个独立的插件,我们都会创建一个独立线程
# 插件实例化
当你的插件代码调用了 self.UCode.extensions.register(extensionRegister)
的时候, 我们会开始实例化插件
积木块注册:
实例化插件的时候, 我们会先实例化 extensionRegister
里面的 BlockRegister
, 并且会调用 getInfo()
注册积木块, 已经 积木块对应的 callback
函数
# 角色实例化
每当用户添加当前插件的角色的时候, 我们会通过积木块对应的信息, 实例化一个 Target(角色对象), 分配一个TargetID(角色 ID), 并且挂载对应的积木块盒子 Toolbox
角色 ID
角色 ID 是随机生成的, 而且每次生成的 ID 都不一样, 不要尝试持久化该 ID 作为唯一标识
# 硬件设备化
如果用户没有点击过连接, 这个时候哪怕角色已经创建好了, 但是硬件设备是没有被实例化的, 只有当用户点击连接的那一刻的时候, 我们才会进行实例化 (不管有没有连接成功, 都会实例化)
多个角色的互相影响
每个硬件插件会在一个独立的线程中执行, 但一个硬件插件会被实例化成多个角色, 每个角色都会实例化一次 硬件设备 DeviceConnection, 每个硬件角色和硬件设备都是独立的对象
但是要注意的是, 整个插件的范围是全部角色和硬件设备共享的, 要非常注意一些全局变量的使用
举个例子:
下面这个例子, 在硬件设备定义了一个函数, 这个函数每次调用的时候都会自增 1, 这个 COUNT
是在 DemoDevice
范围以外的, 因此多个角色, 多个硬件之间每次都会共享这个参数, 可能不能达到你想要的目的, 除非你设计的时候就是为了所有硬件实例都共享
let COUNT = 0;
class DemoDevice extends SerialPortProtocol {
count() {
this.send(`${COUNT++}`);
}
}
正确的做法应该是定义在 DemoDevice
成员变量里面, 例如:
class DemoDevice extends SerialPortProtocol {
count = 0;
count() {
this.send(`${COUNT++}`);
}
}
# 角色销毁
当用户删除角色的时候, 会执行 销毁 动作, 会把当前的 积木块工作区 Workspace 清空, 执行 硬件设备 DeviceConnection 的 destroy
销毁动作
但是并不会销毁插件, 也不会注销积木块的注册
# 插件销毁 (暂未完成)
检查: 当用户需要卸载插件的时候, 首先会检索当前是否有已经添加的角色, 如果有的话, 是无法注销的.
积木块注销: 我们会先把所有注册的积木块全部注销
执行销毁函数: 执行插件的销毁函数 destroy