HarmonyOS鸿蒙Next中为什么子线程中Sendable类里面的方法调用同文件外部方法时会崩溃

HarmonyOS鸿蒙Next中为什么子线程中Sendable类里面的方法调用同文件外部方法时会崩溃 条件如下:

  1. 有Sendable注解,但是没有"use shared"
  2. 子线程调用方法
  3. 调用同一个文件的方法

已经知道两种解决方法:1. 增加"use shared" 或者 2. 调用外部文件的方法

但我需要知道这样会崩溃的原因,报错堆栈:

Device info:emulator 
Build info:emulator 5.1.0.212(SP2DEVC00E212R4P11)
Fingerprint:5aa0192db91cc503d28c8e9b03be7aeefdd1c090858fa4cd1cdfbf8e56465f2b
Module name:com.example.myapplication
Version:1.0.0
VersionCode:1000000
PreInstalled:No
Foreground:Yes
Timestamp:2026-01-14 20:14:06.552
Pid:28439
Uid:20020058
Process name:com.example.myapplication
Process life time:37s
Reason:Signal:SIGABRT(SI_TKILL)@0x01317b5a00006f17 from:28439:20020058
LastFatalMessage:[ecmascript] Can not get module: &entry/src/main/ets/sendable_test/SendableTest&
Fault thread info:
Tid:29109, Name:WorkerThread
#00 pc 000000000019ee8c /system/lib/ld-musl-aarch64.so.1(raise+212)(4d7ed00c176ea3b1ba7951cbd92f1e61)
#01 pc 000000000014d294 /system/lib/ld-musl-aarch64.so.1(abort+20)(4d7ed00c176ea3b1ba7951cbd92f1e61)
#02 pc 0000000000603690 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::ModuleManager::HostGetImportedModule(std::__h::basic_string<char, std::__h::char_traits<char>, panda::ecmascript::CAddressAllocator<char>> const&)+524)(54e592432b806967d657be4d9b09693f)
#03 pc 000000000076454c /system/lib64/platformsdk/libark_jsruntime.so(54e592432b806967d657be4d9b09693f)
#04 pc 000000000057e3f8 /system/lib64/module/arkcompiler/stub.an(RTStub_CallRuntime+40)
#05 pc 000000000005bfe0 /system/lib64/module/arkcompiler/stub.an(BCStub_HandleLdlocalmodulevarImm8+104)
#06 at onChanged entry (entry/src/main/ets/sendable_test/SendableTest.ets:33:5)
#07 at anonymous entry (entry/src/main/ets/workers/MyWorker.ets:24:26)
#08 pc 00000000003d8ab4 /system/lib64/platformsdk/libark_jsruntime.so(54e592432b806967d657be4d9b09693f)
#09 pc 0000000000674da8 /system/lib64/platformsdk/libark_jsruntime.so(panda::FunctionRef::CallForNapi(panda::ecmascript::EcmaVM const*, panda::JSValueRef*, panda::JSValueRef* const*, int)+1396)(54e592432b806967d657be4d9b09693f)
#10 pc 000000000005e02c /system/lib64/platformsdk/libace_napi.z.so(napi_call_function+296)(541f5e82e4ba78fea33bd722fe7c2710)
#11 pc 000000000001b348 /system/lib64/module/libworker.z.so(7bd4f25c26605e040d345a948740ced3)
#12 pc 000000000001b1e4 /system/lib64/module/libworker.z.so(7bd4f25c26605e040d345a948740ced3)
#13 pc 0000000000018308 /system/lib64/module/libworker.z.so(7bd4f25c26605e040d345a948740ced3)
#14 pc 000000000001599c /system/lib64/platformsdk/libuv.so(32df3c638e82d1eb1a4b522ade39f08a)
#15 pc 0000000000026a2c /system/lib64/platformsdk/libuv.so(32df3c638e82d1eb1a4b522ade39f08a)
#16 pc 0000000000015ee0 /system/lib64/platformsdk/libuv.so(uv_run+320)(32df3c638e82d1eb1a4b522ade39f08a)
#17 pc 0000000000017d50 /system/lib64/module/libworker.z.so(7bd4f25c26605e040d345a948740ced3)
#18 pc 000000000002b924 /system/lib64/module/libworker.z.so(7bd4f25c26605e040d345a948740ced3)
#19 pc 00000000001be710 /system/lib/ld-musl-aarch64.so.1(4d7ed00c176ea3b1ba7951cbd92f1e61)
#20 pc 00000000000a54a0 /system/lib/ld-musl-aarch64.so.1(4d7ed00c176ea3b1ba7951cbd92f1e61)

d堆栈太长,其他的省略了

崩溃代码如下(可稳定复现崩溃):

import { lang, worker } from "@kit.ArkTS";
import { fineTest } from "./AnotherTest";

export interface TestKeyChangedListener extends lang.ISendable {
  onChanged(key: string): void;
}

export function initUpgradeListening() {
  const myWorker = new worker.ThreadWorker('entry/ets/workers/MyWorker.ets');
  const listener = new UpgradeChangeListener();
  console.log("initUpgradeListening listener constructed")
  // 通过 postMessage 发送 Sendable 实例(引用传递)
  myWorker.postMessage({ type: 'REGISTER', listener: listener });

  setTimeout(() => {
    if (myWorker) {
      console.info("index Main Thread: Triggering change every 10s...");
      myWorker.postMessage('TRIGGER_CHANGE');
    }
  }, 10000);

}

export function innerTest(manual: boolean): void {
  console.log("innerTest 不会被调用,会在调用之间加载崩溃")
}


@Sendable
class UpgradeChangeListener implements TestKeyChangedListener {
  onChanged(_: string): void {
    // this.ttt();// 不崩溃,调用自己的ttt也是没有问题的
    innerTest(false);//会崩溃,调用自己文件的fuction
    // fineTest();//不崩溃,调用别的文件的function不会出问题
  }

  ttt():void{
    console.log("ttt is ok")
  }
}

initUpgradeListening()在EntryAbility的onWindowStageCreate被调用

MyWorker的主要逻辑是进行子线程回调onChanged

辛苦帮忙分析一下~~


更多关于HarmonyOS鸿蒙Next中为什么子线程中Sendable类里面的方法调用同文件外部方法时会崩溃的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

👍

更多关于HarmonyOS鸿蒙Next中为什么子线程中Sendable类里面的方法调用同文件外部方法时会崩溃的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS鸿蒙Next中,Sendable类用于跨线程安全传递数据。当子线程中的Sendable类方法调用同文件外部方法时,崩溃通常是由于线程安全问题或Sendable类的隔离限制导致的。Sendable类在跨线程传递时,其内部方法调用外部非Sendable方法可能违反线程安全规则,引发运行时异常。

这个崩溃的根本原因是HarmonyOS Next中Sendable类的模块隔离机制与线程模型之间的冲突。

从堆栈信息可以看到关键错误:

[ecmascript] Can not get module: &entry/src/main/ets/sendable_test/SendableTest&
#02 pc 0000000000603690 /system/lib64/platformsdk/libark_jsruntime.so(panda::ecmascript::ModuleManager::HostGetImportedModule(...)+524)

崩溃机制分析:

  1. Sendable类的跨线程传递特性:当使用@Sendable注解但没有use shared时,该类实例在跨线程传递时会进行序列化/反序列化,创建新的实例副本。

  2. 模块加载上下文隔离:每个线程(包括Worker线程)有独立的模块加载上下文。当Sendable类的方法在子线程中被调用时,它试图访问原模块(SendableTest)中的函数innerTest()

  3. 模块解析失败:子线程的模块管理器无法找到或加载主线程的模块SendableTest,因为:

    • 模块路径解析基于当前线程的上下文
    • Sendable类副本与原模块的关联在跨线程时丢失
    • 系统无法在子线程的模块命名空间中定位到原模块
  4. 调用自身方法ttt()正常的原因ttt()是类实例方法,随Sendable类一起被序列化传递,不依赖外部模块解析。

  5. 调用外部文件fineTest()正常的原因:外部模块通过import语句显式导入,在子线程中会重新加载该模块,建立了正确的模块依赖关系。

技术本质:这是ArkTS运行时在跨线程Sendable调用时对闭包和模块作用域处理的限制。当Sendable方法尝试调用同文件但非类成员函数时,需要解析原模块的导出表,而跨线程后这个解析上下文丢失。

两种解决方法有效的原理:

  • use shared:改为共享内存模式,避免序列化,保持模块引用
  • 调用外部文件方法:通过import建立明确的模块依赖关系,确保跨线程可加载,
回到顶部