【鸿蒙(HarmonyOS)开发】native层调用ets层函数
前言
最近遇到一个问题,就是鸿蒙如何在native层去调用js的函数,做过Android开发的都知道,在jni当中,提供了获取java类和调用java方法的相关函数,但是呢,在napi当中,我们并没有获取js文件的相关函数,于是乎,在napi当中,如何调用js层的函数,便成为了一个问题。
探索过程
首先,那肯定是网络上搜一波解决方案,毕竟这个也是一个比较常见的功能,然后,通过一顿网络上的搜索,最终找到一个方案,也就是可以传入到native层当中,然后缓存下来,之后再通过缓存的函数指针来调用。
虽然找到了这个解决方案,但是呢,感觉这个方案又不是很优雅,于是乎,问了下其他的鸿蒙开发者,得到的答复依然是,需要缓存后调用,这,这就没有办法了,只能用这一种方案了,这里感谢解答的开发者们。
开发环境
我这里只有9的API,因此这里基于API 9进行测试,对于大于API 9的版本,因为我拿不到对应的sdk,因此无法测试,对于高版本文档,我这也看不了,因此也没办法查阅。
具体环境如上图所示。
实现方案
有了这个方案,实现起来就比较简单,注意参考资料1当中的代码是有点问题的,也有可能是我版本的原因,通过查看文档,最终确定了可以使用的代码。
简单测试
首先,写一个简单的js函数
function addOne(val: number): number {
return val + 1;
}
之后呢,需要写一个注册的函数,将js层的函数传递给native层。
// src/main/cpp/types/libentry/index.d.ts
export const register: (func: Function) => void;
export const callNative: () => void;
然后,在native层缓存函数,并且缓存下来函数指针。
#include "napi/native_api.h"
#include <hilog/log.h>
#define TAG "LQ->"
#define LOGI(...) ((void)OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, TAG, __VA_ARGS__))
#define LOGW(...) ((void)OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, TAG, __VA_ARGS__))
#define LOGD(...) ((void)OH_LOG_Print(LOG_APP, LOG_DEBUG, LOG_DOMAIN, TAG, __VA_ARGS__))
#define LOGE(...) ((void)OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, TAG, __VA_ARGS__))
#define GET_NAPI_ERROR()
const napi_extended_error_info *errorInfo;
napi_status status = napi_get_last_error_info(env, &errorInfo);
if (status == napi_ok) {
LOGE("last error info is %{public}sn", errorInfo->error_message);
} else {
LOGE("get last error erorn");
}
napi_ref js_fun_ref;
static napi_value Register(napi_env env, napi_callback_info info) {
size_t argc = 1;
napi_value argv[1] = {NULL};
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
napi_create_reference(env, argv[0], 1, &js_fun_ref);
napi_value ret;
napi_create_int32(env, 0, &ret);
return ret;
}
static napi_value CallNative(napi_env env, napi_callback_info info) {
napi_value js_fun = NULL;
napi_get_reference_value(g_env, js_fun_ref, &js_fun);
size_t argc = 1;
napi_value argv[1];
napi_create_int32(env, 666, &argv[0]);
napi_value callbackResult = nullptr;
napi_status callRet = napi_call_function(env, nullptr, js_fun, 1, argv, &callbackResult);
if (callRet != napi_ok) {
GET_NAPI_ERROR();
}
if (callbackResult != nullptr) {
int resultValue;
napi_get_value_int32(env, callbackResult, &resultValue);
LOGE("result: %{public}d", resultValue);
}
napi_value ret;
napi_create_int32(env, 0, &ret);
return ret;
}
然后,给注册一下native的函数,这样就完成了
EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
napi_property_descriptor desc[] = {
{"register", nullptr, Register, nullptr, nullptr, nullptr, napi_default, nullptr},
{"callNative", nullptr, CallNative, nullptr, nullptr, nullptr, napi_default, nullptr},
};
napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
g_env = env;
return exports;
}
EXTERN_C_END
然后,看一下,实验的效果,发现还是十分不错的。
进一步思考
这么做的话,如果每一次,我都需要传入一个函数指针进来,那么将会非常的麻烦,因此呢,要思考一种方式,能够动态的支持多个函数的注册,于是乎,我们需要改造一下我们的函数。
static napi_value Register(napi_env env, napi_callback_info info) {
napi_value ret;
napi_create_int32(env, 0, &ret);
size_t argc = 2;
napi_value argv[2] = {NULL};
// 获取到传入的函数名字
void *data;
// 获取JavaScript传递的参数
napi_get_cb_info(env, info, &argc, argv, nullptr, &data);
// 获取字符串的长度
size_t str_length;
napi_get_value_string_utf8(env, argv[0], nullptr, 0, &str_length);
// 因为这个长度不包括, 因此分配缓冲区,加1是为了容纳终止字符 ''
char *buffer = new char[str_length + 1];
napi_get_value_string_utf8(env, argv[0], buffer, str_length + 1, &str_length);
LOGD("func name: %{public}s", buffer);
// 缓存函数
if (g_method_cache.find(buffer) == g_method_cache.end()) {
return ret;
}
napi_get_cb_info(env, info, &argc, argv, NULL, NULL);
napi_ref ref;
napi_create_reference(env, argv[1], 1, &ref);
g_method_cache[buffer] = ref;
return ret;
}
static napi_value CallNative(napi_env env, napi_callback_info info) {
napi_value ret;
napi_create_int32(env, 0, &ret);
const char *buffer = "addOne";
napi_ref js_ref = g_method_cache[buffer];
napi_value js_fun = NULL;
napi_get_reference_value(g_env, js_ref, &js_fun);
size_t argc = 1;
napi_value argv[1];
napi_create_int32(env, 666, &argv[0]);
napi_value callbackResult = nullptr;
napi_status callRet = napi_call_function(env, nullptr, js_fun, 1, argv, &callbackResult);
if (callRet != napi_ok) {
GET_NAPI_ERROR();
}
if (callbackResult != nullptr) {
int resultValue;
napi_get_value_int32(env, callbackResult, &resultValue);
LOGE("result: %{public}d", resultValue);
}
return ret;
}
然后对应的js文件如下
export const callNative: () => void;
export const register: (name: string, func: Function) => void;
这样,就可以动态传入,并且缓存下来了,效果也是可以的。
总结
到这里,在native层调用js的函数也就讲完了,总的来说,写起来还是比较繁琐的,而且需要自己注册和缓存起来,不过这里还有些问题,所有方法都是同步调用的,我并没有处理异步和多线程的情况,有机会再聊,溜了,溜了。
参考资料
-
https://juejin.cn/post/7302029216142376995 -
https://nodejs.cn/api/n-api.html -
https://developer.harmonyos.com/cn/docs/documentation/doc-references-V3/2_1_u6a21_u5757-0000001446810528-V3
原文始发于微信公众号(Coder小Q):【鸿蒙(HarmonyOS)开发】native层调用ets层函数
- 左青龙
- 微信扫一扫
- 右白虎
- 微信扫一扫
评论