1、概 述
本文是在鸿蒙多线程开发——线程间数据通信对象03(sendable)基础上做的扩展讨论。
Sendable协议定义了ArkTS的可共享对象体系及其规格约束。符合Sendable协议的数据(以下简称Sendable对象)可以在ArkTS并发实例间传递。
默认情况下,Sendable数据在ArkTS并发实例间(包括主线程、TaskPool、Worker线程)传递的行为是引用传递。同时,ArkTS也支持Sendable数据在ArkTS并发实例间拷贝传递。
Sendable的使用有一些需要注意的事项,本文针对较常见的情况做讨论。
2、Sendable的使用规则与约束
主要规则有:
-
Sendable class只能继承自Sendable class;
-
非Sendable class只能继承自非Sendable class;
-
非Sendable class只能实现非Sendable interface;
-
Sendable class/interface成员变量必须是Sendable支持的数据类型;
-
Sendable class/interface的成员变量不支持使用!断言;
-
Sendable class/interface的成员变量不支持使用计算属性名;
-
泛型类中的Sendable class,collections.Array,collections.Map,collections.Set的模板类型必须是Sendable类型;
-
Sendable class的内部不允许使用当前模块内上下文环境中定义的变量;
-
Sendable class和Sendable function不能使用除了@Sendable的其它装饰器;
-
不能使用对象字面量/数组字面量初始化Sendable类型;
-
非Sendable类型不可以as成Sendable类型;
-
箭头函数不支持共享;
-
Sendable装饰器修饰类型时仅支持修饰函数类型;
展开说明如下:
👉🏻 Sendable class只能继承自Sendable class
✅ 正确使用案例:
@Sendableclass A {constructor() {}}@Sendableclass B extends A {constructor() {super()}}
❌ 错误使用案例:
class A {constructor() {}}@Sendableclass B extends A {constructor() {super()}}
👉🏻 非Sendable class只能继承自非Sendable class
✅ 正确使用案例:
class A {constructor() {}}class B extends A {constructor() {super()}}
❌ 错误使用案例:
@Sendableclass A {constructor() {}}class B extends A {constructor() {super()}}
👉🏻 非Sendable class只能实现非Sendable interface
✅ 正确使用案例:
interface I {};class B implements I {};
❌ 错误使用案例:
import { lang } from '@kit.ArkTS';type ISendable = lang.ISendable;interface I extends ISendable {};class B implements I {};
👉🏻 Sendable class/interface成员变量必须是Sendable支持的数据类型
✅ 正确使用案例:
@Sendableclass A {constructor() {}a: number = 0;}
❌ 错误使用案例:
@Sendableclass A {constructor() {}b: Array<number> = [1, 2, 3] // 需使用collections.Array}
👉🏻 Sendable class/interface的成员变量不支持使用!断言
✅ 正确使用案例:
@Sendableclass A {constructor() {}a: number = 0;}
❌ 错误使用案例:
@Sendableclass A {constructor() {}a!: number;}
👉🏻 Sendable class/interface的成员变量不支持使用计算属性名
✅ 正确使用案例:
@Sendableclass A {num1: number = 1;num2: number = 2;add(): number {return this.num1 + this.num2;}}
❌ 错误使用案例:
enum B {b1 = "bbb"}@Sendableclass A {["aaa"]: number = 1; // ["aaa"] is allowed in other classes in ets files[B.b1]: number = 2; // [B.b1] is allowed in other classes in ets files}
👉🏻 泛型类中的Sendable class,collections.Array,collections.Map,collections.Set的模板类型必须是Sendable类型
✅ 正确使用案例:
import { collections } from '@kit.ArkTS';try {let arr1: collections.Array<number> = new collections.Array<number>();let num: number = 1;arr1.push(num);} catch (e) {console.error(`taskpool execute: Code: ${e.code}, message: ${e.message}`);}
❌ 错误使用案例:
import { collections } from '@kit.ArkTS';try {let arr1: collections.Array<Array<number>> = new collections.Array<Array<number>>();let arr2: Array<number> = new Array<number>();arr2.push(1);arr1.push(arr2);} catch (e) {console.error(`taskpool execute: Code: ${e.code}, message: ${e.message}`);}
👉🏻 Sendable class的内部不允许使用当前模块内上下文环境中定义的变量
由于Sendable对象在不同并发实例间的上下文环境不同,如果直接访问会有非预期行为。不支持Sendable对象使用当前模块内上下文环境中定义的变量,如果违反,编译阶段会报错。
✅ 正确使用案例:
import { lang } from '@kit.ArkTS';type ISendable = lang.ISendable;interface I extends ISendable {}@Sendableclass B implements I {static o: number = 1;static bar(): B {return new B();}}@Sendableclass C {v: I = new B();u: number = B.o;foo() {return B.bar();}}
❌ 错误使用案例:
import { lang } from '@kit.ArkTS';type ISendable = lang.ISendable;interface I extends ISendable {}@Sendableclass B implements I {}function bar(): B {return new B();}let b = new B();{@Sendableclass A implements I {}@Sendableclass C {u: I = bar(); // bar不是sendable class对象,编译报错v: I = new A(); // A不是定义在top level中,编译报错foo() {return b; // b不是sendable class对象,而是sendable class的实例,编译报错}}}
👉🏻 Sendable class和Sendable function不能使用除了@Sendable的其它装饰器
如果类装饰器定义在ts文件中,产生修改类的布局的行为,那么会造成运行时的错误。
✅ 正确使用案例:
@Sendableclass A {num: number = 1;}
❌ 错误使用案例:
@Sendable@Observedclass C {num: number = 1;}
👉🏻 不能使用对象字面量/数组字面量初始化Sendable类型
Sendable数据类型只能通过Sendable类型的new表达式创建。
✅ 正确使用案例:
import { collections } from '@kit.ArkTS';let arr1: collections.Array<number> = new collections.Array<number>(1, 2, 3); // 是Sendable类型
❌ 错误使用案例:
import { collections } from '@kit.ArkTS';let arr2: collections.Array<number> = [1, 2, 3]; // 不是Sendable类型,编译报错let arr3: number[] = [1, 2, 3]; // 不是Sendable类型,正例,不报错let arr4: number[] = new collections.Array<number>(1, 2, 3); // 编译报错
👉🏻 非Sendable类型不可以as成Sendable类型
✅ 正确使用案例:
class A {state: number = 0;}@Sendableclass SendableA {state: number = 0;}let a1: A = new SendableA() as A;
❌ 错误使用案例:
class A {state: number = 0;}@Sendableclass SendableA {state: number = 0;}let a2: SendableA = new A() as SendableA;
👉🏻 箭头函数不支持共享
✅ 正确使用案例:
@Sendabletype SendableFuncType = () => void;@Sendablefunction SendableFunc() {console.info("Sendable func");}@Sendableclass SendableClass {constructor(f: SendableFuncType) {this.func = f;}func: SendableFuncType;}let sendableClass = new SendableClass(SendableFunc);
❌ 错误使用案例:
@Sendabletype SendableFuncType = () => void;let func: SendableFuncType = () => {}; // 编译报错@Sendableclass SendableClass {func: SendableFuncType = () => {}; // 编译报错}
👉🏻 Sendable装饰器修饰类型时仅支持修饰函数类型
Sendable装饰器修饰类型时仅支持修饰函数类型。
✅ 正确使用案例:
@Sendabletype SendableFuncType = () => void;
❌ 错误使用案例:
@Sendabletype A = number; // 编译报错@Sendableclass C {}@Sendabletype D = C; // 编译报错
3、HAR中使用Sendable
在HAR中使用Sendable时,需开启编译生成TS文件的配置。
修改方式:在HAR模块下的module.json5文件中将"metadata"字段下的"name"设置为“UseTsHar”,配置如下所示:
{"module": {"name": "TsClosedHar","type": "har","deviceTypes": ["default","tablet","2in1"],"metadata": [{"name": "UseTsHar","value": "true"}]}}
4、与TS/JS交互的规则
4.1、ArkTS通用规则(目前只针对Sendable对象)
-
Sendable对象传入TS/JS的接口中,禁止操作其对象布局(增、删属性,改变属性类型)。
-
Sendable对象设置到TS/JS的对象上,TS中获取到这个Sendable对象后,禁止操作其对象布局(增、删属性,改变属性类型)。
-
Sendable对象放入TS/JS的容器中,TS中获取到这个Sendable对象后,禁止操作其对象布局(增、删属性,改变属性类型)。
4.2、NAPI规则(目前只针对Sendable对象)
-
禁止删除属性,不能使用的接口有:napi_delete_property。
-
禁止新增属性,不能使用的接口有:napi_set_property、napi_set_named_property、napi_define_properties。
-
禁止修改属性类型,不能使用的接口有:napi_set_property、napi_set_named_property、napi_define_properties。
-
不支持Symbol相关接口和类型,不能使用的接口有:napi_create_symbol、napi_is_symbol_object、napi_symbol。
5、与UI交互的规则
Sendable数据需要与makeObserved联用,才可以观察Sendable对象的数据变化。