鸿蒙开发编译运行问题解决方案

一、编译时报错"找不到符号"

1.1 NDK工程编译时找不到符号

问题现象

NDK工程使用CMake编译时,报链接错误,找不到符号:

typescript 复制代码
ld.lld: error: undefined symbol:XXX

背景知识

在项目中引入C++源码构建so并调用,参考使用命令行CMake构建NDK工程。在项目中引用其他使用HarmonyOS工具链编译好的so,请参考在NDK工程中使用预构建库。使用其他工具构建HarmonyOS可用的so,参考通过lycium工具快速编译三方库。

问题定位

场景一:未找到的符号是项目工程里的符号

  • 可能的原因是编译时只依赖了对应的头文件,没有将对应的实现编译进so导致
  • 可按如下步骤排查:使用命令行CMake构建NDK工程
  • 在IDE工程里通过'Find in Files'(默认快捷键CTRL+SHIFT+F)查找工程是否包含了该符号对应的C/CPP文件
  • 在工程编译的CMakeLists.txt里查看,编译时依赖的源码文件是否加入了该文件
  • 可能是编译缓存或者构建文件损坏导致的

场景二:未找到的符号是预构建库中的符号

  • 按如下步骤排查:
  • 参考引入预构建库的指导文档,检查预构建so是否正确引入工程并在NDK的CMakeLists.txt中被依赖
  • 通过file命令查看预构建库的ABI信息,是否为软链接文件,是否为HarmonyOS工程支持的arm64版本
vbnet 复制代码
arm64-v8a/lib# file libxxx.so
libxxx.so: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, BuildID[sha1]=xxxxxxxxx, not stripped
  • 对预构建的so,使用nm命令查看未找到的符号,是否包含在so导出的符号中(strip的库无法查看符号)
arduino 复制代码
# nm libxxx.so
0000000000000270 r abitag
000000000000aa78 t add_format_xxx
000000000000ab98 t add_sheet_xxx
000000000000ab1c t add_xf_xxx
  • 通过objdump或者readelf命令,查看预构建so是否还依赖了其他so库
yaml 复制代码
# objdump -x libxxx.so | grep NEEDED
NEEDED               libc.so
# readelf -d libxxx.so
Dynamic section at offset 0x15b48 contains 24 entries:
Tag        Type                         Name/Value
0x0000000000000001 (NEEDED)             Shared library: [libc.so]
  • 通过源码或者so符号表查看该符号是否是C语言符号,在引入C++工程时,有没有使用extern C声明

分析结论

场景一

  • 使用HarmonyOS工具链构建so时,由于参数配置的原因,导致符号对应的文件没有编译或者导出,在so中找不到符号
  • 编译缓存没有更新,或者构建文件损坏

场景二

  • 依赖预构建库so,只引入了头文件,没有引入so及其依赖的so,或者so依赖路径不对,导致链接时找不到函数的实现
  • 预构建so不是使用HarmonyOS工具链编译的,或者不是arm64版本的,在NDK工程不可用,无法解析其中的符号
  • 对预构建的C语言so,在C++工程里引用头文件时,没有使用extern C声明,C++函数编译生成的符号和C不同,因此在对应的so中无法找到对应的实现
  • 引入的头文件版本和预构建so的源码版本不一致,函数的实现和头文件不一致(如参数个数、类型等),导致从头文件中引用的符号找不到

修改建议

场景一

  • 使用NDK构建工程时,参考官方示例和CMake语法手册,添加函数的实现文件到库编译脚本中
scss 复制代码
file(GLOB CPP_FILES "./.cpp")
add_library(xxx SHARED
  ${CPP_FILES}
  "./XX/xx.cpp"
)
  • 通过IDE工具栏Build > Clean Project后,再次编译工程

场景二

  • 使用预构建库,库以及其依赖的库,都需要使用HarmonyOS工具链构建arm64版本,编译参数可参考应用在其他平台使用的构建参数,并参考预构建库文档将so引入工程并添加依赖
  • 使用系统C API时,也需要将对应的so添加到依赖中
  • 需要注意的是,构建的三方so,其.so文件可能只是个软连接,需要将其链接的bin文件一并导入工程
scss 复制代码
target_link_libraries(libxxx PUBLIC libace_napi.z.so libz.so)
  • C++文件引用C语言的头文件时,需要用extern C声明包裹
arduino 复制代码
extern "C" {
  #include "xxx.h"
}

1.2 其他"找不到符号"问题

问题现象

在鸿蒙中遇到Cannot resolve symbol '$string:microphone_permission_reason'等类似错误。

解决方案

  1. 检查字符串资源定义entry/src/main/resources/base/element/string.json文件中,确保已定义相关字符串:
json 复制代码
{
  "string": [
    {
      "name": "microphone_permission_reason",
      "value": "需要麦克风权限以实现语音录制功能"
    }
  ]
}
  1. 确认资源路径和命名 确保资源文件放在正确的目录下,如entry/src/main/resources/base/element/string.json
  2. 检查权限声明 确认权限的声明是否正确,如在config.json中添加麦克风权限:
json 复制代码
"reqPermissions": [
  {
    "name": "ohos.permission.MICROPHONE"
  }
]
  1. 清理IDE缓存 通过File -> Invalidate Caches清理缓存并重启IDE。
  2. 更新开发工具版本 确保使用最新稳定版的DevEco Studio和HarmonyOS SDK。

二、依赖冲突问题

2.1 多so相互依赖场景下的解耦

问题现象

A模块包含a.soB模块包含b.so。a.so中有调用b.so的函数,b.so中也有调用a.so的函数。如果按照正常编译步骤,无论先编译哪个so,均会编译失败。

解决措施

通过dlopen和dlsym接口进行so编译依赖解耦,将隐式依赖转为显式依赖。

示例代码

a.cpp

arduino 复制代码
extern "C" {
#include "a.h"
#include <dlfcn.h>
#include "stdio.h"

typedef int (*FUNC_SUB)(int, int);

int add(int a, int b) {
    return a + b;
}

int getb(char *path, int a, int b) {
    // path:从ArkTS侧传递So文件的沙箱路径
    void *handle = dlopen(path, RTLD_LAZY);  // 打开一个动态链接库
    if (!handle) {
        return 0;
    }
    FUNC_SUB sub_func = (FUNC_SUB)dlsym(handle, "sub"); // 获取函数名为sub的函数
    int res = sub_func(a, b);                           // 调用函数
    dlclose(handle);                                    // close动态链接库
    return res;
}
}

a.h

arduino 复制代码
extern "C" {
#ifndef DemoSO_a_H
#define DemoSO_a_H

int add(int a, int b);
int getb(char *path, int a, int b);

#endif // DemoSO_a_H
}

b.cpp

arduino 复制代码
extern "C" {
#include "b.h"
#include <dlfcn.h>
#include "stdio.h"

typedef int (*FUNC_ADD)(int, int);

int sub(int a, int b) {
    return a - b;
}

int geta(char *path, int a, int b) {
    // path:从ArkTS侧传递So文件的沙箱路径
    void *handle = dlopen(path, RTLD_LAZY);    // 打开一个动态链接库
    if (!handle) {
        return 0;
    }
    FUNC_ADD add_func = (FUNC_ADD)dlsym(handle, "add");  // 获取函数名为add的函数
    int res = add_func(a, b);                            // 调用函数
    dlclose(handle);                                     // close动态链接库
    return res;
}
}

b.h

arduino 复制代码
extern "C" {
#ifndef DemoSO_b_H
#define DemoSO_b_H

int sub(int a, int b);
int geta(char *path, int a, int b);

#endif // DemoSO_b_H
}

CMakeLists.txt

scss 复制代码
cmake_minimum_required(VERSION 3.4.1)
project(DemoSO)

set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})

include_directories(${NATIVERENDER_ROOT_PATH}
                    ${NATIVERENDER_ROOT_PATH}/include)

add_library(a SHARED a.cpp)
target_link_libraries(a PUBLIC libace_napi.z.so libhilog_ndk.z.so)

add_library(b SHARED b.cpp)
target_link_libraries(b PUBLIC libace_napi.z.so libhilog_ndk.z.so)

add_library(demoso SHARED hello.cpp)
target_link_libraries(demoso PUBLIC a b libace_napi.z.so)

2.2 依赖管理与冲突解决

2.2.1 依赖添加与同步

在HarmonyOS鸿蒙Next中,依赖的添加和重新同步主要通过oh-package.json5文件进行管理。

添加依赖 :在oh-package.json5文件的dependencies属性中添加所需的依赖项

json 复制代码
{
  "dependencies": {
    "example-library": "1.0.0"
  }
}

重新同步依赖:在项目根目录下执行以下命令以重新同步依赖

复制代码
ohpm install

更新依赖 :修改oh-package.json5文件中的版本号,然后再次执行ohpm install命令

删除依赖 :从oh-package.json5文件的dependencies属性中移除不需要的依赖项,然后执行ohpm install命令以同步更改

2.2.2 多Module依赖关系

在HarmonyOS鸿蒙Next中,多Module的依赖关系通过oh-package.json5文件管理。每个Module声明自身依赖的其他Module或第三方库。

本地模块依赖:可以通过配置本地文件夹路径或本地模块名来添加本地模块依赖

json 复制代码
// 配置本地文件夹路径
"dependencies": {
  "folder": "file:../folder"
}

// 配置本地模块名(从DevEco Studio 6.0.0 Beta1版本开始支持)
"dependencies": {
  "moduleA": "@module:moduleA"
}

HAR/HSP包依赖

json 复制代码
// 引用HAR
"dependencies": {
  "package": "file:../package.har"
}

// 引用HSP(在release模式下,构建HSP会生成tgz包)
"dependencies": {
  "package": "file:../package.tgz"
}

注意事项

  • HarmonyOS不支持传递依赖。如果A依赖B,B依赖C,A需要在oh-package.json5中显式写明对C的依赖关系
  • 这种设计是为了避免幽灵依赖问题,确保依赖关系的可控性和稳定性

2.2.3 依赖冲突解决策略

当遇到依赖冲突时,可以采用以下策略解决:

  1. 版本统一 :在项目级oh-package.json5中使用overrides字段统一指定依赖版本
json 复制代码
{
  "overrides": {
    "moduleA": "1.2.0"
  }
}
  1. 排除依赖:在依赖声明中排除冲突的子依赖
json 复制代码
{
  "dependencies": {
    "moduleA": {
      "version": "1.0.0",
      "exclude": ["conflict-module"]
    }
  }
}
  1. 强制解析:在构建脚本中使用强制解析策略
json 复制代码
{
  "buildProfile": {
    "buildOption": {
      "resolveStrategy": {
        "force": {
          "moduleA": "1.2.0"
        }
      }
    }
  }
}

三、模拟器运行问题

3.1 模拟器启动失败

问题现象

启动模拟器失败,提示"Unable to start the emulator.",模拟器无法启动。

解决方案

  1. 清除模拟器数据:在Local Emulator的设备列表窗口,点击"Wipe User Data"清除模拟器数据,然后重新启动模拟器
  2. 重新下载模拟器镜像:在File > Settings > SDKs > HarmonyOS界面中,卸载并重新下载模拟器镜像文件"System-image"后,尝试重新启动模拟器
  3. 重新下载模拟器应用:在File > Settings > SDKs > HarmonyOS界面中,卸载并重新下载模拟器应用"EmulatorX86"后,尝试重新启动模拟器
  4. 检查系统资源:确保电脑有足够的内存和磁盘空间运行模拟器
  5. 检查虚拟化技术:确保已启用CPU虚拟化技术
  6. 更新显卡驱动:更新或回退显卡驱动到稳定版本

3.2 模拟器启动黑洞:Hyper-V与资源争夺

问题现象

点击模拟器后持续卡在启动页,或提示"未开启Hyper-V"。

快速定位

  • 检查CPU虚拟化支持:任务管理器→性能→CPU→确认"虚拟化"状态为"已启用"
  • 验证Hyper-V激活:Windows功能中勾选"Hyper-V"与"Windows虚拟机监控程序平台",重启生效

根治方案

  • 强制重置模拟器:删除当前模拟器(Device Manager→Delete),重新创建时勾选"Force Cold Boot"
  • 资源释放:关闭其他虚拟机进程(如VMware),避免内存抢占
  • 驱动回退:如果最新版本的显卡驱动有问题,尝试将驱动回退到稳定版本

3.3 模拟器常见问题汇总

3.3.1 HAXM安装问题

在Intel CPU的Windows电脑下,如果提示"Unable to install HAXM",可能是因为Hyper-V功能未关闭导致的。解决方法是关闭Hyper-V功能并重启电脑。

3.3.2 用户数据或本地文件问题

可以通过Wipe User Data清理模拟器用户数据,或者Delete删除已创建的Local文件夹来尝试解决问题。

3.3.3 CPU虚拟化未开启

需要确保CPU虚拟化功能已开启。

3.3.4 显卡驱动或配置问题

如果遇到模拟器黑屏无响应或闪退的情况,可以尝试禁用一个显卡后重新打开模拟器。此外,如果是多显卡设备,可能存在兼容性问题。

3.3.5 磁盘空间不足

确保有足够的磁盘空间供模拟器运行。

3.3.6 Vulkan问题

如果模拟器大小显示为特定数值但无法启动,可能是Vulkan问题,替换vulkan-1.dll文件可能恢复正常。

3.3.7 内存完整性开关问题

对于Mac用户,点击内核隔离,将内存完整性的开关打开可能解决问题。

3.3.8 显卡异常或OpenGL版本过低

检查电脑与显卡的连接是否有异常,以及显卡驱动是否正确安装。

3.3.9 虚拟化特性不支持

警告信息表明,并非所有现代X86虚拟化特性都得到支持,这可能会导致在运行HarmonyOS时出现性能下降的问题。

3.3.10 系统资源占用高

关闭系统资源占用高的其他程序以使系统保持更好性能。

3.3.11 模拟器启动方式错误

如果模拟器一直卡在updating indexes,可能是因为工程太大或工程含有java一些文件导致的。

四、真机运行问题

4.1 真机连接调试无法识别

问题现象

调试运行时,安装HAP失败并提示"设备未找到或未连接";或DevEco Studio设备列表显示"No device"(未识别设备)。

可能原因

  • 设备未开启"开发者选项"开关
  • 设备系统与DevEco Studio版本不匹配
  • 使用的USB连接线为充电线而非数据线
  • 当前的USB数据口损坏
  • hdc工具的进程或设备异常
  • 链接的设备不在支持调试的设备列表中

解决措施

  1. 开启开发者选项和USB调试 在设备上打开"开发者选项"开关,打开"USB调试"开关或"无线调试"开关。
  2. 确认版本匹配 务必确认版本的配套关系是否与当前所使用的开发套件是一致的,可参考版本概览使用对应的配套版本。
  3. 检查USB数据线和接口 请更换为符合USB2.0标准的数据线;建议直接连接,不要使用拓展坞。请更换USB数据口后重新尝试,并检查端口驱动是否正常。
  4. 重启ADB服务 执行如下命令,结束hdc进程,然后重新连接
bash 复制代码
hdc kill

如果按上一个步骤操作后仍无法连接,请重启设备,尝试重新连接。

  1. 其他解决方案
  • 重启设备,连接USB,开启USB调试
  • 确保连接调试的设备在支持列表中
  • 检查ADB设备授权
  • 更新或重装Deveco Studio
  • 使用无线调试(备用方案)
  • 验证HarmonyOS兼容性
  • 联系华为技术支持

4.2 特定芯片设备调试问题

问题现象

例如:使用高通芯片的设备无法进行真机调试,而使用麒麟芯片的设备可以正常调试。

解决方案

  1. 确认USB调试设置 确保已正确开启"开发者选项"和"USB调试"开关。
  2. 安装高通芯片专用驱动 针对高通芯片设备,需要安装专用的驱动程序。
  3. 重启ADB服务
bash 复制代码
hdc kill
hdc start-server
  1. 检查ADB设备授权 确保设备已授权当前计算机进行调试。
  2. 更新或重装Deveco Studio 确保使用最新版本的开发工具。
  3. 使用无线调试(备用方案) 通过无线方式连接设备进行调试。
  4. 验证HarmonyOS兼容性
  • 访问华为开发者论坛或文档,查询HarmonyOS版本对骁龙芯片的调试支持情况
  • 确认项目配置中compileSdkVersiontargetSdkVersion与设备系统版本匹配
  1. 联系华为技术支持 通过华为开发者支持提交问题,提供设备型号、系统版本及错误日志。

五、编译构建优化与问题排查

5.1 编译构建常见问题

5.1.1 依赖库下载失败

问题现象:提示"Install js dependencies failed"或"Gradle sync failed"等错误。

解决方案

  1. 清理缓存 执行File -> Invalidate Caches,删除~/.gradle/caches目录
  2. 仓库重置 修改build.gradle的maven仓库地址为华为镜像源
  3. 版本对齐 检查JDK版本(推荐JDK 11+)与HarmonyOS SDK路径配置

5.2 编译配置优化

5.2.1 构建配置优化

hvigor-config.json5properties下新增ohos.arkCompile.noEmitJs字段,用于指定ArkTS编译过程中是否生成js中间产物,不生成js中间产物可以降低编译过程的峰值内存,加快编译速度

json 复制代码
{
  "properties": {
    "ohos.arkCompile.noEmitJs": true
  }
}

5.2.2 多环境依赖处理

使用hvigor的getOverridessetOverridessetProperty能力,可以统一在项目根目录下的hvigor.ts中直接判断不同的环境进行依赖管理

javascript 复制代码
// hvigor.ts
export default {
  overrides: (context) => {
    const env = context.getProperty('env');
    if (env === 'production') {
      return {
        'moduleA': '2.0.0'
      };
    } else {
      return {
        'moduleA': '1.0.0'
      };
    }
  }
}

六、实用工具与资源

6.1 AI辅助开发工具

CodeGenie是华为推出的AI智能辅助开发助手,深度集成在DevEco Studio中,提供鸿蒙知识智能问答、鸿蒙ArkTS代码补全/生成和万能卡片生成等功能。

主要功能

  • 鸿蒙知识智能问答
  • 代码补全与生成
  • 代码解释功能
  • 万能卡片生成

使用方法: 在DevEco Studio右侧边栏点击"CodeGenie"或输入快捷键"Alt/Option+U"进入CodeGenie。

6.2 调试工具

6.2.1 HDC工具

HDC(HarmonyOS Device Connector)是HarmonyOS应用开发中用于设备连接、调试的命令行工具。

常用命令

perl 复制代码
# 查看已连接设备
hdc list targets

# 安装应用
hdc install app.hap

# 卸载应用
hdc uninstall com.example.app

# 启动应用
hdc shell am start -n com.example.app/.MainAbility

# 查看应用日志
hdc shell hilog | grep com.example.app

# 传输文件
hdc file send local_file remote_path

# 进入设备shell
hdc shell

6.2.2 DevEco Profiler

DevEco Profiler是HarmonyOS应用性能分析工具,可用于分析应用的启动时间、内存使用、CPU占用等性能指标。

主要功能

  • 启动时间分析
  • 内存使用分析
  • CPU占用分析
  • 网络请求分析
  • 功耗分析

七、案例分析与最佳实践

7.1 "找不到符号"案例

案例背景

在NDK工程中使用CMake编译时,提示找不到某个符号,该符号位于项目中的C++文件中。

问题定位

通过检查发现,该C++文件没有被添加到CMakeLists.txt的编译列表中,导致编译时无法找到对应的符号实现。

解决方案

修改CMakeLists.txt文件,添加该C++文件到编译列表中:

css 复制代码
add_library(native-lib SHARED
        src/main/cpp/native-lib.cpp
        src/main/cpp/missing-file.cpp  # 添加缺失的文件
)

7.2 依赖冲突案例

案例背景

项目中同时依赖了两个不同版本的相同库,导致编译冲突。

解决方案

在项目级oh-package.json5文件中使用overrides字段统一指定依赖版本:

json 复制代码
{
  "overrides": {
    "library-name": "2.0.0"
  }
}

7.3 模拟器启动失败案例

案例背景

模拟器启动时卡在开机界面,无法正常启动。

问题定位

查看启动日志,发现是由于显卡驱动版本过高导致的兼容性问题。

解决方案

将显卡驱动回退到稳定版本,重启电脑后问题解决。

7.4 真机调试识别问题

案例背景

使用高通芯片的设备无法被DevEco Studio识别。

解决方案

安装高通芯片专用驱动,并重启ADB服务:

bash 复制代码
hdc kill
hdc start-server

八、总结与注意事项

8.1 编译问题注意事项

  1. 符号找不到问题

    • 确保所有源文件都已添加到编译配置中
    • 检查头文件引用和extern "C"声明
    • 清理编译缓存并重新编译
  2. 依赖冲突问题

    • 使用ohpm管理依赖
    • 明确声明所有依赖,包括间接依赖
    • 使用overrides字段统一依赖版本

8.2 运行调试注意事项

  1. 模拟器问题

    • 确保启用CPU虚拟化技术
    • 分配足够的内存和磁盘空间
    • 保持显卡驱动版本稳定
  2. 真机调试问题

    • 使用原装数据线
    • 安装对应芯片的驱动程序
    • 检查开发者选项和USB调试设置

8.3 最佳实践

  1. 项目配置管理

    • 使用版本控制工具管理项目配置文件
    • 定期更新DevEco Studio和SDK到最新稳定版本
    • 保持依赖版本一致,避免版本冲突
  2. 问题排查流程

    • 查看详细日志信息
    • 逐步排查可能的原因
    • 记录问题解决过程,形成知识库
  3. 社区资源利用

    • 积极参与华为开发者论坛讨论
    • 关注官方文档和更新日志
    • 参加鸿蒙开发者培训和活动

通过遵循上述解决方案和最佳实践,可以有效解决鸿蒙开发中常见的编译和运行问题,提高开发效率,确保应用的稳定运行。

鸿蒙开发学习

相关推荐
leon_teacher4 小时前
HarmonyOS权限管理应用
android·服务器·前端·javascript·华为·harmonyos
鸿蒙先行者8 小时前
鸿蒙调试与日志问题之日志输出不完整解决方案
harmonyos
鸿蒙小灰8 小时前
鸿蒙开发Bundle配置信息总结
harmonyos
lpfasd12310 小时前
鸿蒙OS与Rust整合开发流程
华为·rust·harmonyos
HarmonyOS_SDK12 小时前
云闪付联合HarmonyOS SDK打造更便捷安全的支付体验
harmonyos
Jackson_Li15 小时前
文本转语音?我们来盘一盘(鸿蒙开发)
harmonyos
鸿蒙先行者1 天前
鸿蒙应用开发问题之Ability生命周期管理问题
harmonyos
小小小小小星1 天前
鸿蒙FA/PA架构设计方法论与技术探索
架构·harmonyos
在下历飞雨2 天前
七夕到了,我让AI用Kuikly写了个“孤寡青蛙“App,一码五端真丝滑!
harmonyos