北向应用集成三方库——应用如何调用C/C++三方库

简介

OpenHarmony上的应用一般都是js/ets语言编写的,而js/ets语言是无法直接调用C/C++接口的,所以我们应用如果需要调用C/C++三方库的话,需要在js/ets和C/C++之间建立一个可以互通的桥梁。OpenHarmony系统中提供的napi框架正是这么一座桥梁。

应用调用C/C++三方库的方式

  • so形式调用

    通过OpenHarmony的SDK编译,将三方库编译成so,和系统固件一起打包到系统rom中。
  • hap形式调用

    将三方库源码和应用源码放在一起,通过IDE编译最终打包到应用hap包中。

应用调用C/C++三方库实战

本文通过openjpeg三方库以hap形式调用为例进行说明应用是如何调用C/C++三方库的。

移植适配

openjpeg三方库的移植适配参考文档通过IDE集成C/C++三方库.

Napi接口开发

三方库napi的接口一般是由需求方提供的,对于无需求或需要自己定义接口的,我们可以根据三方库对外导出的API接口进行封装或是根据原生库的测试用例对外封装测试接口。本文中我们以封装2个openjpeg测试接口为例详细说明napi接口开发的具体流程。

napi接口开发前提是需要创建一个napi的工程,具体步骤参考通过Deveco Studio创建一个Napi工程

定义napi接口

根据原生库的测试用例,我们封装2个测试用例接口

typedef struct {
   int comps_num;           // the number of components of the image.
    int comps_prec;         // number of bits per component per pixel
    int img_width;          // the image width
    int img_height;         // the image height
    int title_width;        // width of tile
    int title_height;       // height of title
    int irreversible;       // 1 : use the irreversible DWT 9-7
                            // 0 : use lossless compression (default)
    int cblockw_init;       // initial code block width, default to 64
    int cblockh_init;       // initial code block height, default to 64
    int numresolution;      // number of resolutions
    int offsetx;            // x component offset compared to the whole image
    int offsety;            // y component offset compared to the whole image
    int is_rand;            // Whether to generate data randomly
    char file[256];         // output filename
} J2K_Info;

int OpenjpegCompress(const char *input_file, char *output_file)   # 图片压缩成J2K格式
int openjpeg_create_j2k(J2K_Info *info)                           # 创建一张J2K格式图片

napi接口注册

napi_property_descriptor desc[] = {
        {"openjpeg_compress", nullptr, OpenjpegCompress, nullptr, nullptr,
          nullptr, napi_default, nullptr},
        {"openjpeg_create_j2k", nullptr, OpenjpegCreateJ2K , nullptr, nullptr,
          nullptr, napi_default, nullptr}
    };

napi接口实现

  • openjpeg_compress接口的实现

    static napi_value OpenjpegCompressMethod(napi_env env, napi_callback_info info)
    {
      napi_value result = nullptr;
      napi_get_undefined(env, &result);
      napi_value value;
      size_t argc = 2;
      napi_value args[2];
      size_t size;
      char input_file[256] = {0};
      char output_file[256] = {0};
      
      if (napi_get_cb_info(env, info, &argc, args, nullptr, nullptr) != napi_ok) {  // 获取参数
          return result;
      }
      
      if (napi_get_value_string_utf8(env, args[0], input_file, sizeof(input_file),
                                     &size) != napi_ok) {                           // js类型转换成C/C++类型
          return result;
      }
      
      if (napi_get_value_string_utf8(env, args[1], output_file, sizeof(output_file),
                                     &size) != napi_ok) {                           // js类型转换成C/C++类型
          return result;
      }
    
      if (OpenjpegCompress(input_file, output_file) != 0) {                       // 三方库实现调用的业务逻辑接口
          return result;
      }
    
      if (napi_create_int64(env, 0, &result) != napi_ok) {                        // 创建返回的js类型参数
          std::cout << "napi_create_int64" << std::endl;
      }
    
      return result;                                                               // 返回最终结果。
    }
    
  • openjpeg_create_j2k接口的实现

    static napi_value OpenjpegCreateJ2K(napi_env env, napi_callback_info info)
    {
      napi_value result = nullptr;
      napi_get_undefined(env, &result);
      napi_value value;
      size_t argc = 1;
      J2K_Info j2kInfo;
    
      if (napi_get_cb_info(env, info, &argc, &value, nullptr, nullptr) != napi_ok) {  // 获取参数
          return result;
      }
      
      if (OpenjpegGetJ2kInfo(env, value, &j2kInfo) < 0) {                             // 解析参数
          return result;
      }
    
      if (OpenjpegCreateJ2K(&j2kInfo) < 0) {                                          // 三方库实现调用的业务逻辑接口
          return result;
      }
      
      if (napi_create_int64(env, 0, &result) != napi_ok) {                           // 创建返回的js类型参数
          std::cout << "napi_create_int64" << std::endl;
      }
      
      return result;                                                                 // 返回最终结果。
    }
    
  • 解析参数接口OpenjpegGetJ2kInfo实现:

    static int OpenjpegGetJ2kInfo(napi_env env, napi_value value, J2K_Info *info)
    {
      if (info == nullptr) {
          return -1;
      }
      if(GetObjectPropetry(env, value,"output_file", STRING, info->file) != napi_ok) {
          return -1;
      }
      if (GetObjectPropetry(env, value,"comps_prec", NUMBER, &info->comps_prec) !=
          napi_ok) {
          return -1;
      }
      if (GetObjectPropetry(env, value,"img_width", NUMBER, &info->img_width) !=
          napi_ok) {
          return -1;
      }
      if (GetObjectPropetry(env, value,"img_height", NUMBER, &info->img_height) !=
          napi_ok) {
          return -1;
      }
      if (GetObjectPropetry(env, value,"title_width", NUMBER, &info->title_width) !=
          napi_ok) {
          return -1;
      }
      if (GetObjectPropetry(env, value,"title_height", NUMBER, &info->title_height) !=
          napi_ok) {
          return -1;
      }
      if (GetObjectPropetry(env, value,"irreversible", NUMBER, &info->irreversible) !=
          napi_ok) {
          return -1;
      }
      GetObjectPropetry(env, value,"cblockw_init", NUMBER, &info->cblockw_init);
      GetObjectPropetry(env, value,"cblockh_init", NUMBER, &info->cblockh_init);
      GetObjectPropetry(env, value,"numresolution", NUMBER, &info->numresolution);
      GetObjectPropetry(env, value,"offsetx", NUMBER, &info->offsetx);
      GetObjectPropetry(env, value,"offsety", NUMBER, &info->offsety);
      GetObjectPropetry(env, value,"is_rand", BOOLEAN, &info->is_rand);
      
      return 0;
    }
    

    由上代码可以看出,OpenjpegGetJ2kInfo接扣调用了一个封装的接口GetObjectPropetry,该接口实现了通过调用napi的接口获取对应的数据:

    static int GetObjectPropetry(napi_env env, napi_value object, std::string key, int keyType, void *retValue) {
      napi_value property = nullptr;
      napi_value result = nullptr;
      bool flag = false;
      int ret = -1;
    
      if (napi_create_string_utf8(env, key.c_str(), strlen(key.c_str()), &property)
          != napi_ok) {                                                               // 通过字符串获取napi_value对象
          return ret;
      }
    
      if (napi_has_property(env, object, property, &flag) != napi_ok && flag == true) { // 判断该字符串是否对应由属性值
          return ret;
      }
    
      if (napi_get_property(env, object, property, &result) != napi_ok) {             // 获取字符串对应的属性值
          return ret;
      }
      
      if (keyType == NUMBER) {
          int64_t value = 0;
          if (napi_get_value_int64(env, result, &value) != napi_ok) {               // JS数据类型转换成C/C++的int数据类型
              return ret;
          }
          *(int *)retValue = value;
          ret = 0;
      } else if (keyType == BOOLEAN) {
          bool value = false;
          if (napi_get_value_bool(env, result, &value) != napi_ok) {              // JS数据类型转换成C/C++ 的bool数据类型
              return ret;
          }
          *(int *)retValue = (value == true ? 1 : 0);
      }else if (keyType == STRING) {
          size_t s = 0;
          char buf[256] = {0};
          if (napi_get_value_string_utf8(env, result, buf, sizeof(buf), &s) !=
              napi_ok) {                                                          // JS数据类型转换成C/C++的string数据类型
              return ret;
          }
          strncpy((char *)retValue, buf, strlen(buf));
          ret = 0;
      }
    
      return 0;
    }
    

应用调用napi接口

  • 接口声明

    在确定需要封装的接口后,我们需要将这些接口定义在index.d.ts文件中(路径entry/src/main/cpp/types/libentry/index.d.ts)

    export const openjpeg_compress: (srcName:string, desName:string) =>number;
    interface openjpegOption{
      comps_num:number                // the number of components of the image.
      comps_prec:number               // number of bits per component per pixel
      img_width:number                // the image width
      img_height:number               // the image height
      title_width:number              // width of tile
      title_height:number             // height of title
      irreversible:number             // 1 : use the irreversible DWT 9-7, 
                      // 0 : use lossless compression (default)
      output_file:string              // output filename
      cblockw_init?:number            // initial code block width, default to 64
      cblockh_init?:number            // initial code block height, default to 64
      numresolution?:number           // number of resolutions
      offsetx?:number                 // x component offset compared to the whole image
      offsety?:number                 // y component offset compared to the whole image
      is_rand?:boolean                // Whether to generate data randomly
    }
    export const openjpeg_create_j2k: (option:openjpegOption) => number
    
  • 导入so文件

    import testNapi from "libentry.so"
    
  • JS应用调用接口

    在ets工程中创建2个按钮,并通过按钮调用相关的接口,具体代码如下:

    Button(this.buttonTxt0)
    .fontSize(50)
    .margin({top:30})
    .fontWeight(FontWeight.Normal)
    .onClick(() => {
      testNapi.openjpeg_compress(this.dir + "test_pic.bmp", this.dir + "result.j2k")
    })
    Button(this.buttonTxt1)
    .fontSize(50)
    .margin({top:30})
    .fontWeight(FontWeight.Normal)
    .onClick(() => {
      testNapi.openjpeg_create_j2k({comps_num:3,comps_prec:8,
                                    img_width:2000,img_height:2000,
                                    title_width:1000,title_height:1000,
                                    irreversible:1, output_file:this.dir +
                                    "newImage.j2k"})
    })
    

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙

  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。
  • 想要获取更多完整鸿蒙最新学习知识点,请移步前往小编:https://gitee.com/MNxiaona/733GH/blob/master/jianshu
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容