鸿蒙HarmonyOS NEXT开发:常见性能优化场景-(减少应用内存) ...

打印 上一主题 下一主题

主题 1805|帖子 1805|积分 5415

往期鸿蒙全套实战精彩文章必看内容:



  • 鸿蒙开发核心知识点,看这篇文章就够了
  • 最新版!鸿蒙HarmonyOS Next应用开发实战学习门路
  • 鸿蒙HarmonyOS NEXT开发技能最全学习门路指南
  • 鸿蒙应用开发实战项目,看这一篇文章就够了(部分项目附源码)

简介

随着期间的发展,应用越来越复杂,占用的内存也在不断膨胀,而内存作为系统的稀缺资源比较有限,因此,主动减少应用内存的占用对于整个系统至关紧张。当应用步伐占用过多内存时,系统可能会频仍进行内存接纳和重新分配,导致应用步伐的性能下降,甚至出现崩溃和卡顿的情况。通过减少应用内存的占用,可以有效进步应用的性能和相应速率,节省系统资源,让装备的运行服从更高,延长装备的续航时间。开发者应该在应用开发过程中注重内存管理,积极接纳措施来减少内存占用,以优化应用步伐的性能和用户体验。
HarmonyOS提供了一些内存管理的工具和接口,资助开发者有效地管理内存资源。其中,onMemoryLevell接口可以监听系统内存的变化,并做根据系统内存的实时情况,动态地调解应用步伐的内存,以避免内存过度占用导致的性能标题。LRUCache用于在缓存空间不够的时候,将近期最少利用的数据替换为新数据。在生命周期管理中,可以释放不再利用的系统资源,包罗应用内存、监听变乱、网络句柄等。在Purgeable Memory内存管理机制中,开发者可以通过利用干系接口创建PurgeableMemory对象,从而管理purgeable内存。
利用onMemoryLevel监听内存变化

onMemoryLevel是HarmonyOS提供监听系统内存变化的接口,开发者可以通过onMemoryLevel监听内存变化,从而调解应用的内存。


  • AbilityStage:当HAP中的代码初次被加载到进程中的时候,系统会先创建AbilityStage实例,系统决定调解内存时,再回调AbilityStage实例的onMemoryLevel方法。


  • UIAbility:Ability是UIAbility的基类,在Ability中,提供系统内存变化的回调方法。
  • EnvironmentCallback:EnvironmentCallback模块提供应用上下文ApplicationContext对系统情况变化监听回调的本领。
MemoryLevel分为MEMORY_LEVEL_MODERATE、MEMORY_LEVEL_LOW和MEMORY_LEVEL_CRITICAL三种。其中,MEMORY_LEVEL_MODERATE代表当前系统内存压力适中,应用可以正常运行而不会受到太大影响,MEMORY_LEVEL_LOW代表当前系统的内存已经比较低了,应用应该释放不必要的内存资源,避免造成系统卡顿,MEMORY_LEVEL_CRITICAL代表当前所剩的系统内存非常告急,应用应该尽可能释放更多的资源,以确保系统的稳固性和性能。开发人员应该根据不同的内存级别来接纳相应的措施,如释放资源、优化内存利用等,以确保应用在不同内存状态下都能正常运行。MemoryLevel详细品级定义如下所示:
表1 onMemoryLevel品级定义   品级
  值
  分析
  MEMORY_LEVEL_MODERATE
  0
  系统内存适中。系统可能会开始根据LRU缓存规则杀死进程。
  MEMORY_LEVEL_LOW
  1
  系统内存比较低。此时应该去释放掉一些不必要的资源以提升系统的性能。
  MEMORY_LEVEL_CRITICAL
  2
  系统内存很低。此时应当尽可能地去释放任何不必要的资源,因为系统可能会杀掉所有缓存中的进程,并且开始杀掉应当保持运行的进程,比如背景运行的服务。
  分析
   背景已冻结的应用,AbilityStage、UIAbility、EnvironmentCallback的onMemoryLevel都不可以进行回调。
  利用LRUCache优化ArkTS内存

LRU(Least Recently Used, 近来最少利用)是一种常见的算法,其核心头脑是基于时间局部性原理,即假如一个数据在近来被访问过,那么它在未来被访问的概率也会比较高。
LRUCache是ArkTS中常用的工具函数,是基于LRU实现的缓存工具,常用于缓存一些频仍访问的数据,例如常用的图片、网络哀求的效果等。LRUCache通过维护一个缓存空间来存储数据,当缓存空间不敷时,会根据LRU算法将近来最少利用的数据替换掉,以保证缓存空间的有效利用。因此,LRUCache会根据数据的访问顺序来进行数据替换,优先淘汰最久未被访问的数据。
原理先容

LRUCache通过LinkedHashMap来实现LRU的,LinkedHashMap继续于HashMap,HashMap用于快速查找数据,LinkedHashMap双向链表用于记录数据的顺序关系。所以,对于get、put、remove等操纵,LinkedHashMap除了包含HashMap的功能,还需要实现调解Entry顺序链表的工作。其数据布局如下图所示:
图1 LRUCache的LinkedHashMap数据布局图

LruCache中将LinkedHashMap的顺序设置为LRU顺序,链表头部的对象为近期最少用到的对象。常用的方法及其分析如下所示:


  • 调用get方法:根据key查询对应,假如没有查到则返回null。查询到对应对象后,将该对象移到链表的尾端,并返回查询的对象。
  • 调用put方法:会添加key-value的键值对到缓存中,也是将新对象存储在链表尾端。当内存缓存到达设定的最大值时,将链表头部的对象移除。假如key已经存在,则更新当前key对应的value。
  • 调用remove方法:删除key对应的缓存value,假如key对应的value不在,则返回为null,否则,返回已删除的key-value键值对。
  • 调用updateCapacity方法:设置缓存存储的容量,假如新设置的容量newCapacity小于之前的容量capacity,则只保存newCapacity巨细的数据。
参考案例

缓存工具类可以被计划成一个工具类,其中包含LRUCache单例以及一些操纵LRUCache的方法,如添加数据、获取数据、删除数据等。通过创建一个静态方法来获取LRUCache实例,并在内部进行实例化,可以保证全局只有一个LRUCache对象。通过缓存工具类,各组件之间可以方便地共享缓存数据,避免重复创建缓存实例和数据冗余。这样不仅可以进步系统的性能和服从,还可以减少内存占用和提升数据访问速率。
  1. import { util } from '@kit.ArkTS';
  2. export class LRUCacheUtil {
  3.   private static instance: LRUCacheUtil;
  4.   private lruCache: util.LRUCache<string, Object>;
  5.   private constructor() {
  6.     this.lruCache = new util.LRUCache(64);
  7.   }
  8.   // 获取LRUCacheUtil的单例
  9.   public static getInstance(): LRUCacheUtil {
  10.     if (!LRUCacheUtil.instance) {
  11.       LRUCacheUtil.instance = new LRUCacheUtil();
  12.     }
  13.     return LRUCacheUtil.instance;
  14.   }
  15.   // 判断lruCache缓存是否为空
  16.   public isEmpty(): boolean {
  17.     return this.lruCache.isEmpty();
  18.   }
  19.   // 获取lruCache的容量
  20.   public getCapacity(): number {
  21.     return this.lruCache.getCapacity();
  22.   }
  23.   // 重新设置lruCache的容量
  24.   public updateCapacity(newCapacity: number) {
  25.     this.lruCache.updateCapacity(newCapacity);
  26.   }
  27.   // 添加缓存到lruCache中
  28.   public putCache(key: string, value: Object) {
  29.     this.lruCache.put(key, value);
  30.   }
  31.   // 删除key对应的缓存
  32.   public remove(key: string) {
  33.     this.lruCache.remove(key);
  34.   }
  35.   // 获取key对应的缓存
  36.   public getCache(key: string): Object | undefined {
  37.     return this.lruCache.get(key);
  38.   }
  39.   // 判断是否包含key对应的缓存
  40.   public contains(key: string): boolean {
  41.     return this.lruCache.contains(key);
  42.   }
  43.   // 清除缓存数据,并重置lruCache的大小
  44.   public clearCache() {
  45.     this.lruCache.clear();
  46.     this.lruCache.updateCapacity(64);
  47.   }
  48. }
复制代码
在对应的组件中设置缓存,示例代码如下所示:
  1. import { LRUCacheUtil } from '../utils/LRUCacheUtil';
  2. @Entry
  3. @Component
  4. struct Index {
  5.   @State message: string = 'Hello World';
  6.   aboutToAppear(): void {
  7.     let lruCache = LRUCacheUtil.getInstance();
  8.     // 添加一个<key, value>到lrucache
  9.     lruCache.putCache('nation',10);
  10.     // 再添加一个<key, value>到lrucache
  11.     lruCache.putCache('menu',8);
  12.     // 通过key查询value
  13.     let result0 = lruCache.getCache('2') as number;  
  14.     console.log('result0:' + result0);
  15.     // 从当前缓冲区中删除指定的键及其关联的值
  16.     let result1 = lruCache.remove('2');  
  17.     console.log('result1:' + result1);
  18.     // 检查当前缓冲区是否包含指定的对象
  19.     let result2 = lruCache.contains('1');  
  20.     console.log('result2:' + result2);
  21.     // 设置新的容量大小
  22.     let result4 = lruCache.updateCapacity(110);  
  23.     console.log('result4:' + result4);
  24.   }
  25.   build() {
  26.     Row() {
  27.       Column() {
  28.         Text(this.message)
  29.           .fontSize(50)
  30.           .fontWeight(FontWeight.Bold)
  31.       }
  32.       .width('100%')
  33.     }
  34.     .height('100%')
  35.   }
  36. }
复制代码
同时,可以通过onMemoryLevel监听内存的变化,进而设置对应清理缓存的机制,示例代码如下所示:
  1. import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
  2. import { hilog } from '@kit.PerformanceAnalysisKit';
  3. import { window } from '@kit.ArkUI';
  4. import { LRUCacheUtil } from '../utils/LRUCacheUtil';
  5. export default class EntryAbility extends UIAbility {
  6.   // 监听内存的变化
  7.   onMemoryLevel(level: AbilityConstant.MemoryLevel): void {
  8.     // 根据内存的变化执行内存管理策略
  9.     if (level === AbilityConstant.MemoryLevel.MEMORY_LEVEL_CRITICAL) {
  10.       console.log('The memory of device is critical, release memory.');
  11.       if (!LRUCacheUtil.getInstance().isEmpty()) {
  12.         LRUCacheUtil.getInstance().clearCache();
  13.       }
  14.     }
  15.   }
  16.   
  17.   onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
  18.     hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate');
  19.   }
  20.   onDestroy(): void {
  21.     hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy');
  22.   }
  23.   ...
  24. };
复制代码
利用生命周期管理优化ArkTS内存

组件的生命周期,指的是组件自身的一些可自实行的方法,这些方法会在特殊的时间点或遇到一些特殊页面行为时被自动触发而实行。
原理先容

开发过程中,开发人员可以通过管理对象的生命周期来释放资源、销毁对象、优化ArkTS内存。


  • 在UIAbility组件生命周期中,调用对应生命周期的方法,创建或销毁资源。如在Create或Foreground方法中创建资源,在Background、Destroy方法中销毁对应的资源。
  • 在页面生命周期中,调用对应生命周期的方法,创建或销毁资源。如在onPageShow方法中创建资源,在onPageHide方法中销毁对应的资源。
  • 在组件生命周期中,调用对应生命周期的方法,创建或销毁资源。如在aboutToAppear方法中创建资源,在aboutToDisappear方法中销毁不再利用的对象、注销不再利用的订阅变乱。
  • 调用组件自带的方法,创建、销毁组件。如调用XComponent的onDestroy方法。
aboutToDisappear中销毁订阅变乱

aboutToDisappear函数会在组件析构销毁之前实行。如下案例所示,在利用完网络管理的网络毗连模块后,取消订阅默认网络状态变化的关照。
  1. import { connection } from '@kit.NetworkKit';
  2. import { BusinessError } from '@kit.BasicServicesKit';
  3. import { CommonConstant as Const } from '../common/Constant';
  4. import { promptAction } from '@kit.ArkUI';
  5. import { Logger } from '../utils/Logger';
  6. @Entry
  7. @Component
  8. struct Index {
  9.   @State networkId: string = Const.NETWORK_ID;
  10.   @State netMessage: string = Const.INIT_NET_MESSAGE;
  11.   @State connectionMessage: string = Const.INIT_CONNECTION_MESSAGE;
  12.   @State netStateMessage: string = '';
  13.   @State hostName: string = '';
  14.   @State ip: string = '';
  15.   private controller: TabsController = new TabsController();
  16.   private netHandle: connection.NetHandle | null = null;
  17.   private netCon: connection.NetConnection | null = null;
  18.   scroller: Scroller = new Scroller();
  19.   aboutToDisappear(): void {
  20.     this.unUseNetworkRegister;
  21.   }
  22.   build() {
  23.     Column() {
  24.       Text($r('app.string.network_title'))
  25.         .fontSize($r('app.float.title_font_size'))
  26.         .fontWeight(FontWeight.Bold)
  27.         .textAlign(TextAlign.Start)
  28.         .margin({ left: Const.WebConstant_TEN_PERCENT })
  29.         .width(Const.WebConstant_FULL_WIDTH)
  30.       Column() {
  31.         Row() {
  32.           Text(Const.MONITOR_TITLE)
  33.             .fontSize($r('app.float.font_size'))
  34.             .margin($r('app.float.md_padding_margin'))
  35.             .fontWeight(FontWeight.Medium)
  36.           Blank()
  37.           Toggle({ type: ToggleType.Switch, isOn: false })
  38.             .selectedColor(Color.Blue)
  39.             .margin({ right: $r('app.float.md_padding_margin') })
  40.             .width($r('app.float.area_width'))
  41.             .height(Const.WebConstant_BUTTON_HEIGHT)
  42.             .onChange((isOn) => {
  43.               if (isOn) {
  44.                 this.useNetworkRegister();
  45.               } else {
  46.                 this.unUseNetworkRegister();
  47.               }
  48.             })
  49.         }
  50.         .height($r('app.float.button_height'))
  51.         .borderRadius($r('app.float.md_border_radius'))
  52.         .margin({ left: $r('app.float.md_padding_margin'), right: $r('app.float.md_padding_margin') })
  53.         .width(Const.WebConstant_NINETY_PERCENT)
  54.         .backgroundColor($r('app.color.text_background'))
  55.         TextArea({ text: this.netStateMessage })
  56.           .fontSize($r('app.float.font_size'))
  57.           .width(Const.WebConstant_NINETY_PERCENT)
  58.           .height(Const.WebConstant_FIVE_HUNDRED)
  59.           .margin($r('app.float.md_padding_margin'))
  60.           .borderRadius($r('app.float.md_border_radius'))
  61.           .textAlign(TextAlign.Start)
  62.           .focusOnTouch(false)
  63.         Button($r('app.string.clear'))
  64.           .fontSize($r('app.float.font_size'))
  65.           .width(Const.WebConstant_NINETY_PERCENT)
  66.           .height($r('app.float.area_height'))
  67.           .margin({
  68.             left: $r('app.float.md_padding_margin'),
  69.             right: $r('app.float.md_padding_margin'),
  70.             bottom: $r('app.float.xxl_padding_margin')
  71.           })
  72.           .onClick(() => {
  73.             this.netStateMessage = '';
  74.           })
  75.         Blank()
  76.       }
  77.       .height(Const.WebConstant_FULL_HEIGHT)
  78.       .justifyContent(FlexAlign.Start)
  79.     }
  80.     .width(Const.WebConstant_FULL_WIDTH)
  81.   }
  82.   getConnectionProperties() {
  83.     connection.getDefaultNet().then((netHandle: connection.NetHandle) => {
  84.       connection.getConnectionProperties(netHandle, (error: BusinessError, connectionProperties: connection.ConnectionProperties) => {
  85.         if (error) {
  86.           this.connectionMessage = Const.CONNECTION_PROPERTIES_ERROR;
  87.           Logger.error('getConnectionProperties error:' + error.code + error.message);
  88.           return;
  89.         }
  90.         this.connectionMessage = Const.CONNECTION_PROPERTIES_INTERFACE_NAME + connectionProperties.interfaceName
  91.           + Const.CONNECTION_PROPERTIES_DOMAINS + connectionProperties.domains
  92.           + Const.CONNECTION_PROPERTIES_LINK_ADDRESSES + JSON.stringify(connectionProperties.linkAddresses)
  93.           + Const.CONNECTION_PROPERTIES_ROUTES + JSON.stringify(connectionProperties.routes)
  94.           + Const.CONNECTION_PROPERTIES_LINK_ADDRESSES + JSON.stringify(connectionProperties.dnses)
  95.           + Const.CONNECTION_PROPERTIES_MTU + connectionProperties.mtu + '\n';
  96.       })
  97.     });
  98.   }
  99.   useNetworkRegister() {
  100.     this.netCon = connection.createNetConnection();
  101.     this.netStateMessage += Const.REGISTER_NETWORK_LISTENER;
  102.     this.netCon.register((error) => {
  103.       if (error) {
  104.         Logger.error('register error:' + error.message);
  105.         return;
  106.       }
  107.       promptAction.showToast({
  108.         message: Const.REGISTER_NETWORK_LISTENER_MESSAGE,
  109.         duration: 1000
  110.       });
  111.     })
  112.     this.netCon.on('netAvailable', (netHandle) => {
  113.       this.netStateMessage += Const.NET_AVAILABLE + netHandle.netId + '\n';
  114.     })
  115.     this.netCon.on('netBlockStatusChange', (data) => {
  116.       this.netStateMessage += Const.NET_BLOCK_STATUS_CHANGE + data.netHandle.netId + '\n';
  117.     })
  118.     this.netCon.on('netCapabilitiesChange', (data) => {
  119.       this.netStateMessage += Const.NET_CAPABILITIES_CHANGE_ID + data.netHandle.netId
  120.         + Const.NET_CAPABILITIES_CHANGE_CAP + JSON.stringify(data.netCap) + '\n';
  121.     })
  122.     this.netCon.on('netConnectionPropertiesChange', (data) => {
  123.       this.netStateMessage += Const.NET_CONNECTION_PROPERTIES_CHANGE_ID + data.netHandle.netId
  124.         + Const.NET_CONNECTION_PROPERTIES_CHANGE_CONNECTION_PROPERTIES + JSON.stringify(data.connectionProperties) + '\n';
  125.     })
  126.   }
  127.   unUseNetworkRegister() {
  128.     if (this.netCon) {
  129.       this.netCon.unregister((error: BusinessError) => {
  130.         if (error) {
  131.           Logger.error('unregister error:' + error.message);
  132.           return;
  133.         }
  134.         promptAction.showToast({
  135.           message: Const.UNREGISTER_NETWORK_LISTENER_MESSAGE,
  136.           duration: 1000
  137.         });
  138.         this.netStateMessage += Const.UNREGISTER_NETWORK_LISTENER;
  139.       })
  140.     } else {
  141.       this.netStateMessage += Const.UNREGISTER_NETWORK_LISTENER_FAIL;
  142.     }
  143.   }
  144. }
复制代码
利用purgeable优化C++内存

Purgeable Memory是HarmonyOS中native层常用的内存管理机制,可用于图像处理的Bitmap、流媒体应用的一次性数据、图片等。应用可以利用Purgeable Memory存放其内部的缓存数据,并由系统根据淘汰策略同一管理全部的purgeable内存。当系统内存不敷时,系统可以通过扬弃purgeable内存快速接纳内存资源,以释放更多的内存资源给其他应用步伐利用,实现全局高效的缓存数据管理。这种机制可以资助系统更有效地管理内存,进步系统的稳固性和流畅性。
在利用Purgeable内存时,开发者可以调用接口释放Purgeable内存,但需要注意在适当的机遇释放Purgeable内存,以确保内存资源能够得到有效管理,避免内存占用过高导致的性能标题和内存走漏的情况。通过合理利用Purgeable内存,开发者可以更好地管理应用步伐的内存,进步用户体验。
原理先容

Purgeable内存访问流程图如下图所示,在访问Purgeable内存时,首先需要判断当前Purgeable内存的数据是否已经被接纳,假如Purgeable内存已经被接纳了,那么需要先重建数据再利用。在访问Purgeable内存的数据时,Purgeable内存对应的引用计数refcnt加1,在访问Purgeable竣事后,其引用计数refcnt会减1,当引用计数为0的时候,该Purgeable内存可以被系统接纳。
图2 Purgeable内存访问流程图

Purgeable内存接纳流程图如下所示,当引用计数为0时,扬弃掉Purgeable内存中的数据,并标识Purgeable内存已接纳。
图3 Purgeable内存接纳流程图

参考案例

在CMakeLists.txt文件中引入Purgeable对应的动态链接库libpurgeable_memory_ndk.z.so,详细如下所示:
  1. # the minimum version of CMake.
  2. cmake_minimum_required(VERSION 3.4.1)
  3. project(MyNativeApplication)
  4. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
  5. if(DEFINED PACKAGE_FIND_FILE)
  6.     include(${PACKAGE_FIND_FILE})
  7. endif()
  8. include_directories(${NATIVERENDER_ROOT_PATH}
  9.                     ${NATIVERENDER_ROOT_PATH}/include)
  10. add_library(entry SHARED napi_init.cpp)
  11. # 引入libpurgeable_memory_ndk.z.so动态链接库
  12. target_link_libraries(entry PUBLIC libace_napi.z.so libpurgeable_memory_ndk.z.so)
复制代码
引入purgeable_memory文件,并声明创建PurgeableMemory对象需要利用的ModifyFunc函数,调用OH_PurgeableMemory_Create创建PurgeableMemory对象。
在读取PurgeableMemory对象的内容时,需要调用OH_PurgeableMemory_BeginRead,读取竣事时,需要调用OH_PurgeableMemory_EndRead。其中,OH_PurgeableMemory_GetContent可以获取PurgeableMemory对象的内存数据。
在修改PurgeableMemory对象的内容时,需要调用OH_PurgeableMemory_BeginWrite,读取竣事时,需要调用OH_PurgeableMemory_EndWrite。其中,OH_PurgeableMemory_AppendModify可以更新PurgeableMemory对象重建规则。
  1. #include "napi/native_api.h"
  2. #define DATASIZE (4 * 1024 * 1024)
  3. #include "purgeable_memory/purgeable_memory.h"
  4. bool ModifyFunc(void *data, size_t size, void *param) {
  5.     data = param;
  6.     return true;
  7. }
  8. // 业务定义对象类型
  9. class ReqObj;
  10. static napi_value Add(napi_env env, napi_callback_info info)
  11. {
  12.     size_t requireArgc = 2;
  13.     size_t argc = 2;
  14.     napi_value args[2] = {nullptr};
  15.     napi_get_cb_info(env, info, &argc, args , nullptr, nullptr);
  16.     napi_valuetype valuetype0;
  17.     napi_typeof(env, args[0], &valuetype0);
  18.     napi_valuetype valuetype1;
  19.     napi_typeof(env, args[1], &valuetype1);
  20.     double value0;
  21.     napi_get_value_double(env, args[0], &value0);
  22.     double value1;
  23.     napi_get_value_double(env, args[1], &value1);
  24.     double result = value0 + value1;
  25.     // 创建一个PurgeableMemory对象
  26.     OH_PurgeableMemory *pPurgmem = OH_PurgeableMemory_Create(DATASIZE, ModifyFunc, &result);
  27.     // 读取对象
  28.     OH_PurgeableMemory_BeginRead(pPurgmem);
  29.     // 获取PurgeableMemory对象大小
  30.     size_t size = OH_PurgeableMemory_ContentSize(pPurgmem);
  31.     // 获取PurgeableMemory对象内容
  32.     ReqObj *pReqObj = (ReqObj *)OH_PurgeableMemory_GetContent(pPurgmem);
  33.     // 读取PurgeableMemory对象结束
  34.     OH_PurgeableMemory_EndRead(pPurgmem);
  35.     // 修改PurgeableMemory对象
  36.     OH_PurgeableMemory_BeginWrite(pPurgmem);
  37.     // 声明扩展创建函数的参数
  38.     double newResult = value0 + value0;
  39.     // 更新PurgeableMemory对象重建规则
  40.     OH_PurgeableMemory_AppendModify(pPurgmem, ModifyFunc, &newResult);
  41.     // 修改PurgeableMemory对象结束
  42.     OH_PurgeableMemory_EndWrite(pPurgmem);
  43.     // 销毁对象
  44.     OH_PurgeableMemory_Destroy(pPurgmem);
  45.     napi_value sum;
  46.     napi_create_double(env, result, &sum);
  47.     return sum;
  48. }
  49. EXTERN_C_START
  50. static napi_value Init(napi_env env, napi_value exports)
  51. {
  52.     napi_property_descriptor desc[] = {
  53.         { "add", nullptr, Add, nullptr, nullptr, nullptr, napi_default, nullptr }
  54.     };
  55.     napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
  56.     return exports;
  57. }
  58. EXTERN_C_END
  59. static napi_module demoModule = {
  60.     .nm_version = 1,
  61.     .nm_flags = 0,
  62.     .nm_filename = nullptr,
  63.     .nm_register_func = Init,
  64.     .nm_modname = "entry",
  65.     .nm_priv = ((void*)0),
  66.     .reserved = { 0 },
  67. };
  68. extern "C" __attribute__((constructor)) void RegisterEntryModule(void)
  69. {
  70.     napi_module_register(&demoModule);
  71. }
复制代码
看完三件事❤️



  • 假如你觉得这篇内容对你还蛮有资助,我想约请你帮我三个小忙:
  • 点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。
  • 关注作者 ,不定期分享原创知识。
  • 同时可以期待后续文章ing

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

雁过留声

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表