# 插件生命周期

每个插件都有自己的生命周期, 你应该时刻关注

# 插件 - 积木块 - 角色 - 硬件设备 的关系

  • 每个插件只会实例化一次
  • 插件实例化的时候, 会实例化一个 BlockExtension
  • BlockExtension 实例化的时候, 会调用 getInfo() 注册积木块以及对应的 callback(opcode 或 func 对应的同名函数)
  • 每次添加硬件角色 (就是同一类角色) , 会实例化一个角色对象, 会挂载 Toolbox 和 积木块 (只是挂载在角色对象上)
  • 一旦角色进行连接操作, 就会实例化 硬件设备, 硬件角色挂载在角色上

可以看下图的对应关系

ucode-extension-target

# 检索

当用户准备添加硬件的时候, 我们会检索对应的 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 清空, 执行 硬件设备 DeviceConnectiondestroy 销毁动作

但是并不会销毁插件, 也不会注销积木块的注册

# 插件销毁 (暂未完成)

  • 检查: 当用户需要卸载插件的时候, 首先会检索当前是否有已经添加的角色, 如果有的话, 是无法注销的.

  • 积木块注销: 我们会先把所有注册的积木块全部注销

  • 执行销毁函数: 执行插件的销毁函数 destroy