HarmonyOS 使用napi方法返回一个对象,多次调用会闪退

代码如下:

Index.ets

import { hilog } from '@kit.PerformanceAnalysisKit';
import testNapi from 'libentry.so';

class DateTime {
  constructor() {
    this.year = 0;
    this.month = 0;
    this.day = 0;
    this.hour = 0;
    this.minute = 0;
    this.second = 0;
    this.milliseconds = 0;
    this.utHourOffset = 0;
    this.utMinuteOffset = 0;
  }

  year: number;
  month: number;
  day: number;
  hour: number;
  minute: number;
  second: number;
  milliseconds: number;
  utHourOffset: number;
  utMinuteOffset: number;
}

@Entry
@Component
struct Index {
  @State message: string = 'Hello World';

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
          .onClick(async () => {
            // 返回复杂对象会崩溃
            let ret = await testNapi.add(2, 3) as DateTime;
            hilog.info(0x0020, 'testTag', 'year:%{public}d, month:%{public}d, day:%{public}d, hour:%{public}d',
              ret.year, ret.month, ret.day, ret.hour);

            // 返回简单对象不会崩溃
            //let ret = await testNapi.add(2, 3) as DateTime;
            //hilog.info(0x0020, 'testTag', 'ret:%{public}d', ret);
          })
      }
      .width('100%')
    }
    .height('100%')
  }
}

Index.d.ts

// 返回复杂对象会崩溃
export const add: (a: number, b: number) => Promise<object>;

// 返回简单对象不会崩溃
//export const add: (a: number, b: number) => Promise<number>;

napi_init.cpp

#include "napi/native_api.h"
#include <cassert>

struct FSDateTime {
    int year;
    int month;
    int day;
    int hour;
    int minute;
    int second;
    int milliseconds;
    int UTHourOffset;
    int UTMinuteOffset;
};

struct stRunTime {
    int nStatus; // FR_SUCCESS代表成功, 负数代表失败
    napi_async_work work;
    napi_deferred deferred;
    napi_ref napiRef;
    stRunTime() : nStatus(0), work(nullptr), deferred(nullptr), napiRef(nullptr) {}
};

static napi_value fsDateTime2JsDateTime(napi_env env, const FSDateTime &fsDateTime) {
    napi_value napiResult = nullptr, napiProperty = nullptr;
    napi_create_object(env, &napiResult);
    napi_create_uint32(env, fsDateTime.year, &napiProperty);
    napi_set_named_property(env, napiResult, "year", napiProperty);
    napi_create_uint32(env, fsDateTime.month, &napiProperty);
    napi_set_named_property(env, napiResult, "month", napiProperty);
    napi_create_uint32(env, fsDateTime.day, &napiProperty);
    napi_set_named_property(env, napiResult, "day", napiProperty);
    napi_create_uint32(env, fsDateTime.hour, &napiProperty);
    napi_set_named_property(env, napiResult, "hour", napiProperty);
    napi_create_uint32(env, fsDateTime.minute, &napiProperty);
    napi_set_named_property(env, napiResult, "minute", napiProperty);
    napi_create_uint32(env, fsDateTime.second, &napiProperty);
    napi_set_named_property(env, napiResult, "second", napiProperty);
    napi_create_uint32(env, fsDateTime.milliseconds, &napiProperty);
    napi_set_named_property(env, napiResult, "milliseconds", napiProperty);
    napi_create_int32(env, fsDateTime.UTHourOffset, &napiProperty);
    napi_set_named_property(env, napiResult, "utHourOffset", napiProperty);
    napi_create_uint32(env, fsDateTime.UTMinuteOffset, &napiProperty);
    napi_set_named_property(env, napiResult, "utMinuteOffset", napiProperty);

    return napiResult;
}
static void jsExecuteCallback(napi_env env, void *data) {
    stRunTime *pRunTime = (stRunTime *)data;
    pRunTime->nStatus = 0;

    // 返回复杂对象会崩溃
    FSDateTime fsDateTime = {1, 2, 3, 4, 5, 6, 7, 8, 9};
    napi_value napiRet = fsDateTime2JsDateTime(env, fsDateTime);

    // 返回简单对象不会崩溃
    // napi_value napiRet;
    // assert(napi_ok == napi_create_int32(env, 111, &napiRet));
    assert(napi_ok == napi_create_reference(env, napiRet, 1, &pRunTime->napiRef));
}
static void jsCompleteCallback(napi_env env, napi_status status, void *data) {
    stRunTime *pRunTime = (stRunTime *)data;
    napi_value result = nullptr;

    if (pRunTime->nStatus == 0) {
        assert(napi_ok == napi_get_reference_value(env, pRunTime->napiRef, &result));
    } else {
        assert(napi_ok == napi_get_undefined(env, &result));
    }
    if (pRunTime->deferred != nullptr) {
        if (pRunTime->nStatus == 0) {
            assert(napi_ok == napi_resolve_deferred(env, pRunTime->deferred, result));
        } else {
            assert(napi_ok == napi_reject_deferred(env, pRunTime->deferred, result));
        }
    }
    if (pRunTime->napiRef != nullptr) {
        assert(napi_ok == napi_delete_reference(env, pRunTime->napiRef));
    }
    assert(napi_ok == napi_delete_async_work(env, pRunTime->work));
    delete pRunTime;
    pRunTime = nullptr;
}
static napi_value Add(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);

    napi_valuetype valuetype0;
    napi_typeof(env, args[0], &valuetype0);

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);

    double value0;
    napi_get_value_double(env, args[0], &value0);

    double value1;
    napi_get_value_double(env, args[1], &value1);

    stRunTime *pRunTime = new stRunTime();

    napi_value napiRet = nullptr;
    assert(napi_ok == napi_create_promise(env, &pRunTime->deferred, &napiRet));
    napi_value resourceName = nullptr;
    assert(napi_ok == napi_create_string_utf8(env, "add", NAPI_AUTO_LENGTH, &resourceName));
    assert(napi_ok == napi_create_async_work(env, nullptr, resourceName, jsExecuteCallback, jsCompleteCallback,
                                             (void *)pRunTime, &pRunTime->work));
    assert(napi_ok == napi_queue_async_work(env, pRunTime->work));

    return napiRet;
}
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {{"add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "entry",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }
HarmonyOS
1天前
浏览
收藏 0
回答 1
待解决
回答 1
按赞同
/
按时间
zxjiu

多线程安全问题。

assert(napi_ok == napi_create_async_work(env, nullptr, resourceName, jsExecuteCallback, jsCompleteCallback,
                                         (void *)pRunTime, &pRunTime->work));

napi_create_async_work的第四个参数jsExecuteCallback,其是在业务线程执行,jsExecuteCallback函数在调用fsDateTime2JsDateTime和napi_create_reference函数时,env是主线程的env,创建的napi_value和napi_ref类型的变量都是在子线程中,所以会崩溃。

问题的分析定位,请参考方舟多线程检测:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/ide-multi-thread-check-0000001919872114-V5

分享
微博
QQ
微信
回复
1天前
相关问题
HarmonyOS 使用Webview退
19浏览 • 1回复 待解决
HarmonyOS number toFixed方法退
40浏览 • 1回复 待解决
HarmonyOS 页面返回时应用退报错
627浏览 • 1回复 待解决
打开图库应用时偶尔退
2437浏览 • 0回复 待解决
HarmonyOS C++ 调用 ets 层函数报错退
49浏览 • 1回复 待解决
HarmonyOS应用退问题
732浏览 • 1回复 待解决
HarmonyOS webview加载html string退
35浏览 • 1回复 待解决
HarmonyOS 如何返回一个颜色?
312浏览 • 1回复 待解决