# akitest **Repository Path**: lian-bacteria/akitest ## Basic Information - **Project Name**: akitest - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-01-03 - **Last Updated**: 2026-01-03 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # hsp1的接口与调用 ## 1. 向entry提供so接口 hsp1将xxx1.so放在hsp1\libs\arm64-v8a文件夹下。安装到系统后,entry先找到hsp1中的so地址("/data/storage/el1/bundle/libs/arm64/xxx1.so"),然后通过dlopen打开so库。 注意: so库中的C++函数一定一定要用extern "C" 括起来,否则会dlopen失败!!!!例如 ``` extern "C" { // 一定一定要用extern "C" 括起来,否则会dlopen失败!!!! double sub(double a, double b) { return a + b; } } ``` ## 2. 调用hsp2中的so接口 hsp2将xxx2.so放在hsp2\libs\arm64-v8a文件夹下。安装到系统后,hsp1先找到hsp2中的so地址,然后通过dlopen打开so库,如下所示。 ``` //dlopen hsp2的so char *path = new char[1024]; size_t size = 1024; strcpy(path, "/data/storage/el1/bundle/libs/arm64/xxx2.so"); void *handle = dlopen(path, RTLD_LAZY); // 打开一个动态链接库.路径为path if (!handle) { return 0; } Mul mul_func = (Mul)dlsym(handle, "xxx"); // 示例:获取函数名为xxx的函数 float res = mul_func(c, d); dlclose(handle); // 最后记得close动态库 ``` 注意: ![输入图片说明](1.png) 应用沙箱路径和真实物理路径的对应关系如下图所示,具体链接:[应用沙箱目录-应用文件-Core File Kit(文件基础服务)-应用框架 - 华为HarmonyOS开发者 (huawei.com)](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/app-sandbox-directory-V5#%E5%BA%94%E7%94%A8%E6%B2%99%E7%AE%B1%E8%B7%AF%E5%BE%84%E5%92%8C%E7%9C%9F%E5%AE%9E%E7%89%A9%E7%90%86%E8%B7%AF%E5%BE%84%E7%9A%84%E5%AF%B9%E5%BA%94%E5%85%B3%E7%B3%BB) ![输入图片说明](2.png) # hsp2的接口与调用 ## 1. 向hsp1提供so接口 hsp2将xxx2.so放在hsp2\libs\arm64-v8a文件夹下。安装到系统后,hsp1先找到hsp2中的so地址("/data/storage/el1/bundle/libs/arm64/xxx2.so"),然后通过dlopen打开so库。 注意: so库中的C++函数一定一定要用extern "C" 括起来,否则会dlopen失败!!!!例如 ``` extern "C" { // 一定一定要用extern "C" 括起来,否则会dlopen失败!!!! double sub(double a, double b) { return a + b; } } ``` ## 2. C++接口与ArkTS接口的绑定 ### 第一步:依赖配置 源码依赖(推荐) 指定cpp路径下(如:/hsp2/src/main/cpp) ``` cd hsp2/src/main/cpp git clone https://gitee.com/openharmony-sig/aki.git ``` CMakeLists.txt添加依赖(假定编译动态库名为:libhello.so): ``` add_subdirectory(aki) target_link_libraries(hello PUBLIC aki_jsbind) ``` ### 第二步:在Index.d.ts中导出JSBind.bindFunction接口 ``` export class JSBind { static bindFunction: (name: string, func: Function) => number; } ``` ### 第三步:在ArkTS侧利用JSBind.bindFunction绑定JS函数 示例如下,实际开发代码时可绑定所需要的函数 ``` import libAddon from 'libhello.so' // 插件名为hello function sayHelloFromJS (value) { console.log('what do you say: ' + value); return "hello from JS" } libAddon.JSBind.bindFunction("sayHelloFromJS", sayHelloFromJS); ``` ### 第四步:在C++侧调用上一步绑定的JS函数 ``` #include #include void DoSomething() { // 索引 JS 函数句柄 auto jsFunc = aki::JSBind::GetJSFunction("sayHelloFromJS"); // Invoke 指定 JS 方法的返回值类型 auto result = jsFunc->Invoke("hello from C++"); // 可在非JS线程执行 // result == "hello from JS" } JSBIND_ADDON(hello) // 注册 AKI 插件名为: hello //注意: //a. 注册的AKI插件名需要与模块级的oh-package.json5文件dependencies标签的“lib”(例如libhello.so)字段名称保持一致 //b. 注册完AKI插件后,才能从ArkTS侧 import aki from 'libhello.so' 导入插件 JSBIND_GLOBAL() { JSBIND_FUNCTION(DoSomething); } //注意: //在JSBIND_GLOBAL作用域下使用JSBIND_FUNCTION绑定C++全局函数后,可从ArkTS侧直接调用 ``` ## 3. UKey业务弹窗接口【已更新】 !!!!注意:通过以下startAbility的方式创建业务弹窗可能会出现以下问题 问题:业务弹窗代码在一个进程里,c侧代码在另一个进程里,导致获取到用户在弹窗中输入的数据后无法直接返回给c侧代码。 建议解决方法:通过共享文件的方式将弹窗数据返回给c侧代码 ### 第一步:依赖配置 源码依赖(推荐) 指定cpp路径下(如:项目根路径/hsp2/src/main/cpp) ``` cd hsp2/src/main/cpp git clone https://gitee.com/openharmony-sig/aki.git ``` CMakeLists.txt添加依赖(假定编译动态库名为:libhello.so): ``` add_subdirectory(aki) target_link_libraries(hello PUBLIC aki_jsbind) ``` ### 第二步:在Index.d.ts中导出JSBind.bindFunction接口 ``` export class JSBind { static bindFunction: (name: string, func: Function) => number; } ``` ### 第三步:在ArkTS侧利用JSBind.bindFunction绑定JS函数【已更新】 在 JsFuncUtils.startPinWinodow函数中,可通过AppStorage.get(" ")取出保存的context,调用startAbility接口,拉起UIAbility,绘制出弹窗。将JsFuncUtils.startPinWinodow函数用JSBind.bindFunction绑定后,C++侧可以直接调用。 !!!注意:使用UIAbility的方式绘制出弹窗的前提是能够获取到一个具有startAbility能力的context。本步骤中的context是从hsp1的EmbeddedUIExtensionAbility中获取的,即在EmbeddedUIExtensionAbility中通过AppStorage.setOrCreate("entryContext", this.context)将context保存下来,然后在hsp2中通过AppStorage.get(" ")取出来。(此方法根据方案架构特定而定。若有其它方法可以获取到context,也可以使用) ``` export function bindFun1(){ libAddon.JSBind.bindFunction("JsFuncUtils.startPinWinodow", JsFuncUtils.startPinWinodow); } export class JsFuncUtils { static startPinWinodow() { let entryContext = AppStorage.get("entryContext") as common.UIAbilityContext let want:Want = { bundleName: 'com.xingye.myapplication', abilityName: 'PinAbility', moduleName: 'hsp2', parameters: { aaa:new Date().getTime(), } } try { entryContext.startAbility(want,(err: BusinessError) => { if (err.code) { // 处理业务逻辑错误 console.error(`startAbility failed, code is ${err.code}, message is ${err.message}`); return; } // 执行正常业务 console.info('startAbility succeed'); }); } catch (err) { // 处理入参错误异常 let code = (err as BusinessError).code; let message = (err as BusinessError).message; console.error(`startAbility failed, code is ${code}, message is ${message}`); } } } ``` 使用UIAbility绘制弹窗参考如下: ``` //hsp2\src\main\ets\PinAbility\PinAbility.ets import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { display, window } from '@kit.ArkUI'; import { BusinessError } from '@kit.BasicServicesKit'; export default class PinAbility extends UIAbility { private windowClass: window.Window | null = null; private formAbility: string = ''; private standardSize = 24; private curBp: string = '' private windowWidth:number = 800 * 1.25; private windowHeight:number = 560 * 1.25; private klvDensityPixels : number = 1.25; //固定窗口只能把这个写死,在不同设备上去调整windowWidth onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onCreate'); } onDestroy(): void { hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onDestroy'); } private updateBreakpoint(windowWidth: number) :void{ // 将长度的单位由px换算为vp let windowWidthVp = windowWidth / display.getDefaultDisplaySync().densityPixels let displayClass = display.getDefaultDisplaySync(); console.log(`aaaaaaaaaaaaa width:${displayClass.width} height:${displayClass.height} dpi:${displayClass.densityDPI}`) let newBp: string = '' if (windowWidthVp < 320) { newBp = 'xs' } else if (windowWidthVp < 600) { newBp = 'sm' } else if (windowWidthVp < 840) { newBp = 'md' } else { newBp = 'lg' } if (this.curBp !== newBp) { this.curBp = newBp // 使用状态变量记录当前断点值 AppStorage.setOrCreate('currentBreakpoint', this.curBp) } } onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); windowStage.loadContent('pages/Pin', (err,data) => { if (err.code) { hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); return; } hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); windowStage.getMainWindow((err: BusinessError, data) => { let errCode: number = err.code; if (errCode) { console.error('Failed to obtain the main window. Cause: ' + JSON.stringify(err)); return; } this.windowClass = data; // 获取应用启动时的窗口尺寸 this.updateBreakpoint(data.getWindowProperties().windowRect.width) console.log("bbbbbbbbbbbbbbbbbbb",data.getWindowProperties().windowRect.height); // 注册回调函数,监听窗口尺寸变化 data.on('windowSizeChange', (windowSize)=>{ this.updateBreakpoint(windowSize.width) }) let prop: window.WindowProperties = this.windowClass.getWindowProperties(); console.info('Succeeded in obtaining the main window. prop: ' + JSON.stringify(prop)); console.info('Succeeded in obtaining the main window. Data: ' + JSON.stringify(data)); //移動位置 this.windowClass.moveWindowTo(950,300) try { let titleButtonArea = this.windowClass.getTitleButtonRect(); console.info('Succeeded in obtaining the area of title buttons. Data: ' + JSON.stringify(titleButtonArea)); } catch (exception) { console.error(`Failed to get the area of title buttons. Cause code: ${exception.code}, message: ${exception.message}`); } if (canIUse("SystemCapability.Window.SessionManager")) { let isTouchable: boolean = false; this.windowClass?.setWindowDecorVisible(isTouchable); } try { this.windowClass?.on('windowTitleButtonRectChange', (titleButtonRect) => { console.info('Succeeded in enabling the listener for window title buttons area changes. Data: ' + JSON.stringify(titleButtonRect)); }); } catch (exception) { console.error(`Failed to enable the listener for window title buttons area changes. Cause code: ${exception.code}, message: ${exception.message}`); } let curBps = AppStorage.get('currentBreakpoint') as string; let percent = this.klvDensityPixels / display.getDefaultDisplaySync().densityPixels this.windowWidth = 400 * 1.25 /percent this.windowHeight = 280 * 1.25 /percent if(curBps == "md"){ this.windowClass.resize(this.windowWidth, this.windowWidth); if (canIUse("SystemCapability.Window.SessionManager")) { this.windowClass.setWindowLimits({ maxWidth: this.windowWidth, maxHeight: this.windowHeight, minWidth: this.windowWidth, minHeight: this.windowHeight }) } }else if (curBps == "lg"){ this.windowClass.resize(this.windowWidth, this.windowWidth); if (canIUse("SystemCapability.Window.SessionManager")) { this.windowClass.setWindowLimits({ maxWidth: this.windowWidth, maxHeight: this.windowHeight, minWidth: this.windowWidth, minHeight: this.windowHeight }) } } }); }); } onWindowStageDestroy(): void { // Main window is destroyed, release UI related resources hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageDestroy'); } onForeground(): void { // Ability has brought to foreground hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onForeground'); } onBackground(): void { // Ability has back to background hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onBackground'); } }; ``` 注意:需要在hsp2的module.json5文件中声明PinAbility ``` { "module":{ ... "abilities": [ { "name": "PinAbility", "srcEntry":"./ets/PinAbility/PinAbility.ets", "startWindowIcon": "$media:startIcon", "startWindowBackground": "$color:start_window_background", "exported": true, } ] ... } } //提示: "startWindowIcon": "$media:startIcon"中引用的startIcon.png需为空白图片,否则弹窗启动的时候会出现图标 ``` ``` //hsp2\src\main\ets\pages\Pin.ets import { common } from '@kit.AbilityKit' @Entry @Preview @Component struct Pin { build() { Column(){ Row(){ Image($r('app.media.key')) .height('30') Text('請輸入U盾密碼') }.justifyContent(FlexAlign.SpaceEvenly) Row(){ TextInput() .borderRadius(0) } Row(){ Button('取消') .onClick(()=>{ //銷毀ability let pinContext : common.UIAbilityContext= getContext(this) as common.UIAbilityContext pinContext.terminateSelf() }) Button('確定') .onClick(()=>{ //1.輸入pin嗎后做的邏輯 //2.銷毀ability let pinContext : common.UIAbilityContext= getContext(this) as common.UIAbilityContext pinContext.terminateSelf() }) } } .justifyContent(FlexAlign.SpaceEvenly) .height('100%') .width('100%') .border({ width: { left: 5, right: 5, top: 5, bottom: 5 }, color: { left: Color.Black, right: Color.Black, top: Color.Black, bottom: Color.Black }, radius: { topLeft: 8, topRight: 8, bottomLeft: 8, bottomRight: 8 }, style: { left: BorderStyle.Solid, right: BorderStyle.Solid, top: BorderStyle.Solid, bottom: BorderStyle.Solid, } }) } } ``` ### 第四步:在C++侧调用上一步绑定的JS函数 C++ 使用aki::JSBind::GetJSFunction获取JsFuncUtils.startPinWinodow函数句柄后,使用Invoke触发调用 ``` double mulAndStartPinWindow(double a,double b) { if (auto startPinWinodow = aki::JSBind::GetJSFunction("JsFuncUtils.startPinWinodow")) { startPinWinodow->Invoke(); // 返回值类型 void } return a * b; } ```