转载注明出处

1 准备一份c代码

这里以cJSON为例,只需要使用到仓库的cJSON.h和cJSON.c

2 创建一个native项目

打开DevEco-Studio创建一个native项目
在这里插入图片描述
选项随意填写
在这里插入图片描述
将cJSON.c和cJSON.h放到项目自动创建的cpp文件夹下
在这里插入图片描述
在cmakelists.txt添加两行
add_library(cjson SHARED cJSON.c)
target_link_libraries(cjson PUBLIC libace_napi.z.so)

cjson表示最终导出的so库的名字(libcjson.so)
cJOSN.c表示使用这个文件编译出动态so链接库

项目使用cmake具体用法可自行查找资料
在这里插入图片描述

3 编译并导出so库

选择构建模块‘entry’
编译Hap
在这里插入图片描述

在这里插入图片描述
在build->outputs->default可找到打包出来的hap
在这里插入图片描述
我们解压这个hap
得到3个平台的so动态链接库
在这里插入图片描述
也可以直接在build->intermediates->libs->default里找到对应平台的so库不需要解压hap
在这里插入图片描述

可在项目的build-profile配置abiFilters,不写默认导出3个平台的so
在这里插入图片描述
现在我们就得到了一份看起来不太聪明的so动态链接库

4 导入第三方so动态库并在ArkTs中使用

创建一个普通项目
在这里插入图片描述
将刚刚生成的3个平台的so库放到libs对应平台的文件夹里,目录没有可手动创建,平台的so不能混用,放对位置。
在这里插入图片描述
在Arkts中使用so库并调用函数

import cjson from 'libcjson.so'

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

  aboutToAppear() {
    let a= cjson;
    console.log(JSON.stringify(cjson));
    cjson.cJSON_CreateObject();
  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

在这里插入图片描述
不出意外,得到一个undefined
ArkTs加载的so库类似node的napi,不能直接调用c的函数需要我们自己封装一层,用于连接ArkTs和c function 可参考官方文档
按照指引我们返回创建的native项目对cJSON.c进行修改,添加一些函数

5 添加注册函数和其他ArkTs与c function交互的函数 N-Api

打开cJSON.c
在文件末尾添加头文件和注册函数

void Fn_cJSON_CreateObject() {
}

#include "napi/native_api.h"

static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"cJSON_CreateObject", NULL, Fn_cJSON_CreateObject, NULL, NULL, NULL, napi_default, NULL}};
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}

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

__attribute__((constructor)) void RegisterEntryModule(void) {
    napi_module_register(&demoModule);
}

desc[]数组每一项都是暴露给ArkTs可以直接调用的函数
你需要在ArkTs中使用到的所有c函数都需要添加进去,用不到的不需要添加
我们暂时暴露cJSON_CreateObject这个函数作为例子
Fn_cJSON_CreateObject名字随意取,cJSON_CreateObject名字是暴露给ArkTs使用的,也是随意取,现在我们修改Fn_cJSON_CreateObject,用于调用c的cJSON_CreateObject函数

napi_value Fn_cJSON_CreateObject(napi_env env, napi_callback_info info) {
    cJSON *cObject = cJSON_CreateObject();

    /*size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {NULL};

    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

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

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

    char *returnStr = "你很棒呀!加油打工人";
    napi_value result;
    napi_create_string_utf8(env, returnStr, strlen(returnStr), &result);

    return result;
}

第一行调用c的cJSON_CreateObject函数
注释部分是用于接收来至ArkTs的参数,我们没传,所以暂时注释掉
这里暂时返回一个 字符串 加油打工人!
重复 第3步和第4步
编译出so库,在新项目中导入替换旧so的并使用
在新项目中修改代码并运行,得到我们想要的预期结果
在这里插入图片描述

import cjson from 'libcjson.so'

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

  aboutToAppear() {
    let a = cjson;
    const result = cjson.cJSON_CreateObject();
    console.log(result)
  }

  build() {
    Row() {
      Column() {
        Text(this.message)
          .fontSize(50)
          .fontWeight(FontWeight.Bold)
      }
      .width('100%')
    }
    .height('100%')
  }
}

好了,现在我们已经成功连接ArkTs和c function了,到这已经结束了

cJSON_CreateObject函数返回的是一个cJson的结构体,我们只返回了一个字符串,对应到ArkTs因该为一个Object对象,接着我们继续修改代码,返回c函数结构体真正对应到ArkTs的Object
查看cJSON.h
cJSON的结构体

typedef struct cJSON
{
    /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
    struct cJSON *next;
    struct cJSON *prev;
    /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */
    struct cJSON *child;

    /* The type of the item, as above. */
    int type;

    /* The item's string, if type==cJSON_String  and type == cJSON_Raw */
    char *valuestring;
    /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */
    int valueint;
    /* The item's number, if type==cJSON_Number */
    double valuedouble;

    /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
    char *string;
} cJSON;

转换成ArkTs的数据结构便是

interface cJSON {
  next: cJSON
  prev: cJSON
  child: cJSON
  type: number
  valuestring: string
  valueint: number
  valuedouble: number
  string: string
}

修改Fn_cJSON_CreateObject

napi_value Fn_cJSON_CreateObject(napi_env env, napi_callback_info info) {
    cJSON *cObject = cJSON_CreateObject();

    /*size_t requireArgc = 2;
    size_t argc = 2;
    napi_value args[2] = {NULL};

    napi_get_cb_info(env, info, &argc, args, NULL, NULL);

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

    napi_valuetype valuetype1;
    napi_typeof(env, args[1], &valuetype1);*/
    napi_value result;

    char *returnStr = "你很棒呀!加油打工人";
    cObject->valuestring = returnStr;

    napi_create_object(env, &result);

    napi_value type;
    napi_create_int32(env, cObject->type, &type);

    napi_value valueint;
    napi_create_int32(env, cObject->valueint, &valueint);

    napi_value valuestring;
    napi_create_string_utf8(env, cObject->valuestring ? cObject->valuestring : "", strlen(cObject->valuestring), &valuestring);

    napi_value string;
    if (cObject->string) {
        napi_create_string_utf8(env, cObject->string, strlen(cObject->string), &string);
    }

    napi_value valuedouble;
    napi_create_double(env, cObject->valuedouble, &valuedouble);

    napi_value next, prev, child;
    napi_create_reference(env, NULL, 1, &next);
    napi_create_reference(env, NULL, 1, &prev);
    napi_create_reference(env, NULL, 1, &child);

    napi_set_named_property(env, result, "type", type);
    napi_set_named_property(env, result, "valueint", valueint);
    napi_set_named_property(env, result, "valuestring", valuestring);
    napi_set_named_property(env, result, "string", string ? string : NULL);
    napi_set_named_property(env, result, "valuedouble", valuedouble);
    napi_set_named_property(env, result, "next", next);
    napi_set_named_property(env, result, "prev", prev);
    napi_set_named_property(env, result, "child", child);

    return result;
}

得到预期结果
在这里插入图片描述

觉得有用的不忘点个赞,转载注明出处,有任何疑问欢迎在评论区提出。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐