命令式节点常见问题
=========

本文档介绍命令式节点的常见问题并提供参考。

#### FrameNode节点运行时出现jscrash

**问题现象**

不规范地使用[FrameNode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-framenode)后出现[JS Crash](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/jscrash-guidelines)。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/af/v3/kAYQ6kDeSFGal0DhsLVS7w/zh-cn_image_0000002625107889.png?HW-CC-KV=V1&HW-CC-Date=20260612T073324Z&HW-CC-Expire=86400&HW-CC-Sign=8C675BC3E33990C7A9BFF5D7D4BEC02CDCF0130E36166FE99B23322E1F8FDC45)

**解决措施**

根据提示跳转至报错日志，查看具体的报错原因，进行相应的修改，具体的跳转方法请参考下方示例代码。

**示例代码**

该示例演示了FrameNode抛出[dispose](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-framenode#dispose12)相关异常的场景。运行示例代码后会出现jscrash报错，参考下方的动图，跳转至具体的报错场景，发现报错的原因是调用dispose后不能调用[getMeasuredSize](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/js-apis-arkui-framenode#getmeasuredsize12)，在本示例中，删除dispose相关代码即可正常运行。

import { NodeController, FrameNode, typeNode } from '@kit.ArkUI';

// 继承NodeController实现自定义UI控制器
class MyNodeController extends NodeController {
  makeNode(uiContext: UIContext): FrameNode | null {
    let node = new FrameNode(uiContext);
    node.dispose(); // 删除本行可以让程序正常运行
    node.getMeasuredSize();
    return node;
  }
}

@Entry
@Component
struct FrameNodeTypeTest {
  private myNodeController: MyNodeController = new MyNodeController();

  build() {
    Row() {
      Text('Hello')
      NodeContainer(this.myNodeController);
    }
  }
}

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/17/v3/wERugm9yT4KQbdeLYqrNZw/zh-cn_image_0000002594828300.png?HW-CC-KV=V1&HW-CC-Date=20260612T073324Z&HW-CC-Expire=86400&HW-CC-Sign=7B92FD704E37A382DD3F1511EAB1D895F9DACEF9A338B0FD42DB3AF729AAD600)

#### Native侧创建的ArkUI\_NodeHandle执行disposeNode后出现cppcrash

**问题现象**

开发者对[ArkUI\_NodeHandle](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-node8h)执行[disposeNode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#disposenode)前，未清理节点相关的资源对象（如回调、捕获引用等），导致节点下树后高概率发生程序崩溃，崩溃原因为释放后使用（Use After Free）。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/11/v3/KnEGwf6xSFaNy2flrrI0bw/zh-cn_image_0000002625187747.png?HW-CC-KV=V1&HW-CC-Date=20260612T073324Z&HW-CC-Expire=86400&HW-CC-Sign=0D3E779C652E6CBE6172A9405E81F01FCEB2FED67E17B2AA34FC67F221C3CC05)

下图为此类问题的典型故障日志，日志中的Reason:Signal字段为SIGSEGV(SEGV\_MAPERR)，表示崩溃地址不固定，可能提示野指针或空指针解引用。此时崩溃栈内各个栈帧基本均为系统栈，如DetachFromMainTree、~FrameNode等系统函数，此类系统函数多与disposeNode接口和节点下树析构相关。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/ab/v3/D_80TUWSRCuWwBjWP2bIZA/zh-cn_image_0000002625107891.png?HW-CC-KV=V1&HW-CC-Date=20260612T073324Z&HW-CC-Expire=86400&HW-CC-Sign=15BE3400EBED7A6CE9D362F7A6026FB7E75EED1EBB8BB9853292C505CE530223)

**解决措施**

调整资源释放顺序，优先释放节点衍生资源（依赖节点创建的对象与回调、捕获引用等），再释放节点。

下面提供一个cppcrash的示例。具体实现为创建[XComponent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/ts-basic-components-xcomponent)时调用BindNode，将TS侧XComponent传入Native侧并创建[OH\_ArkUI\_SurfaceCallback](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-oh-nativexcomponent-native-xcomponent-oh-arkui-surfacecallback)，在XComponent下树时调用UnbindNode回收相关资源。BindNode通过XComponent节点创建[OH\_ArkUI\_SurfaceHolder](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-oh-nativexcomponent-native-xcomponent-oh-arkui-surfaceholder)对象并注册[OH\_ArkUI\_SurfaceCallback\_SetSurfaceDestroyedEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-interface-xcomponent-h#oh_arkui_surfacecallback_setsurfacedestroyedevent)事件。在UnbindNode中，由于XComponent的dispose在OH\_ArkUI\_SurfaceHolder调用dispose之前执行，导致后者释放时使用了已释放的XComponent节点，从而触发cppcrash。

针对上述示例，在UnbindNode函数中，把disposeNode移至函数末尾前执行，即可修复此问题。

void OnSurfaceDestroyedNative(OH\_ArkUI\_SurfaceHolder \*holder)
{
    std::string \*helloWorld = reinterpret\_cast<std::string \*>(OH\_ArkUI\_SurfaceHolder\_GetUserData(holder));
    OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 0xff00, "TestTag", "OnSurfaceDestroyed triggered, registered string is %{public}s",
                 helloWorld->c\_str());
    delete helloWorld;
}

napi\_value UnbindNode(napi\_env env, napi\_callback\_info info)
{
    OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 0xff00, "TestTag", "移除XComponent与衍生资源");
    size\_t argc = 1;
    napi\_value args\[1\] = {nullptr};
    napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    if (!g\_node1) {
        OH\_LOG\_Print(LOG\_APP, LOG\_ERROR, 0xff00, "TestTag", "NodeId does not exist error");
        return nullptr;
    }
    nodeAPI->disposeNode(g\_node1); // 在销毁SurfaceCallback与SurfaceHolder前销毁node，会引发crash
    g\_node1 = nullptr;
    if (g\_holder) {
        OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 0xff00, "TestTag", "Start Dispose SurfaceCallback");
        OH\_ArkUI\_SurfaceHolder\_RemoveSurfaceCallback(g\_holder, g\_callback); // 移除SurfaceCallback
        OH\_ArkUI\_SurfaceCallback\_Dispose(g\_callback);                       // 销毁SurfaceCallback
        g\_callback = nullptr;
    }
    OH\_ArkUI\_SurfaceHolder\_Dispose(g\_holder); // 销毁SurfaceHolder
    g\_holder = nullptr;
    // 将nodeAPI->disposeNode(g\_node1);移至此处即可修复crash
    
    return nullptr;
}

napi\_value BindNode(napi\_env env, napi\_callback\_info info)
{
    size\_t argc = 2;
    napi\_value args\[2\] = {nullptr};
    napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    OH\_ArkUI\_GetNodeHandleFromNapiValue(env, args\[1\], &g\_node1); // 获取nodeHandle
    g\_holder = OH\_ArkUI\_SurfaceHolder\_Create(g\_node1);           // 获取SurfaceHolder
    g\_callback = OH\_ArkUI\_SurfaceCallback\_Create();              // 创建SurfaceCallback
    auto hello = new std::string("helloWorld");
    OH\_ArkUI\_SurfaceHolder\_SetUserData(g\_holder, hello); // 设置std::string至SurfaceHolder
    OH\_ArkUI\_SurfaceCallback\_SetSurfaceDestroyedEvent(g\_callback,
                                                      OnSurfaceDestroyedNative); // 注册OnSurfaceDestroyed回调
    OH\_ArkUI\_SurfaceHolder\_AddSurfaceCallback(g\_holder, g\_callback);             // 注册SurfaceCallback回调
    return nullptr;
}