uni-app 鸿蒙 App 震动失效:ohos.permission.VIBRATE 权限配置全攻略
项目:Vue 3 + uni-app 3.0 App,编译目标 HarmonyOS App
环境:HBuilderX 5.x + DevEco Studio 5.0 + 鸿蒙真机
一、故障现象
做错题时理论上应该触发震动反馈(uni.vibrateLong()),但鸿蒙真机上完全没有任何震动。同样的设备上,微信小程序端的震动是正常的。
代码里有多处震动调用,答错题时触发:
// 答错时震动反馈
if (!isCorrect && settingStore.setting.enableVibrateFeedback) {
uni.vibrateLong()
}
二、排查过程
2.1 第一次尝试:manifest.json 里加 requestPermissions
对比 Android 配置,发现 Android 端有 android.permission.VIBRATE,而鸿蒙端 app-harmony.distribute 下只有图标配置:
// manifest.json
"app-harmony": {
"distribute": {
"icons": { ... } // 只有图标,没有权限
}
}
于是仿照 Android 格式添加:
"app-harmony": {
"distribute": {
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{
"name": "ohos.permission.VIBRATE",
"reason": "用于答错题目时震动反馈及考试倒计时提醒",
"usedScene": { "abilities": ["EntryAbility"], "when": "inuse" }
}
],
"icons": { ... }
}
}
结果:无效。 构建产物 dist/dev/app-harmony/entry/src/main/module.json5 里依然只有 ohos.permission.INTERNET,VIBRATE 完全没出现。
关键发现:uni-app 的鸿蒙构建系统 不会处理
manifest.json里app-harmony.distribute.requestPermissions这个字段。
2.2 第二次尝试:harmony-permissions UTS 插件
DCloud 官方推荐通过 uni_modules 插件声明鸿蒙权限。于是创建了如下结构:
src/uni_modules/harmony-permissions/
└── utssdk/app-harmony/
├── index.uts ← 插件入口
├── module.json5 ← 权限声明
└── resources/base/element/string.json
module.json5 内容:
{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.VIBRATE" }
]
}
}
结果:无效,还引发新问题。
- 构建系统没有合并这个子模块的权限
index.uts文件触发了 UTS 编译,提示Cannot find module: @dcloudio/uni-uts-v1,导致构建直接失败
这条路径彻底走不通。
2.3 第三次排查:深入 native 源码
既然配置层面走不通,回头读运行时实现。在 dist/dev/app-harmony/oh_modules/ 下找到了震动模块的源码:
// VibrateNative.ets(uni-vibrate 鸿蒙实现)
import vibrator from '@ohos.vibrator'
const VIBRATE_ATTRIBUTE: vibrator.VibrateAttribute = {
usage: 'touch' // ← 触摸反馈场景
}
static vibrateLong(): number {
return VibrateNative.startTimeVibration(400) // 400ms 长震动
}
private static startTimeVibration(duration: number): number {
try {
vibrator.startVibration({
type: 'time',
duration
}, VIBRATE_ATTRIBUTE)
.catch((error) => {
console.error('startVibration failed', error.code)
})
return VIBRATE_SUCCESS // ← 永远返回成功!
} catch (error) {
return VibrateNative.mapErrorCode(error)
}
}
这里有两个重要发现:
usage: 'touch'— 官方文档说这种场景不需要ohos.permission.VIBRATE。但我们验证了同一台设备、同一系统设置下微信小程序能震动、独立 App 却不能。错误被吞没 —
startVibration()返回 Promise,但函数永远同步返回VIBRATE_SUCCESS。即使震动失败,.catch也只是console.error,JS 侧完全感知不到。
2.4 关键发现:微信为什么能震
微信小程序能震动,是因为小程序运行在微信的容器里。微信作为系统级 App,本身就持有 ohos.permission.VIBRATE。小程序调用 wx.vibrateLong() 时,实际上是微信的 native 层代为执行震动,用的是微信的权限,所以不需要小程序自己声明。
而独立 App 用自己的 bundle identity,即使 usage: 'touch' 理论上不需要权限,在 HarmonyOS 5.0 某些行为下实际仍需在 module.json5 中显式声明。
三、最终方案:构建后注入
既然 uni-app 构建不会自动写入 VIBRATE 权限,那就在构建完成后、DevEco Studio 打开前自动注入。
3.1 注入脚本
scripts/inject-harmony-permissions.js:
const fs = require('fs')
const path = require('path')
const MODULE_JSON5 = path.join(__dirname, '..', 'dist', 'dev', 'app-harmony',
'entry', 'src', 'main', 'module.json5')
if (!fs.existsSync(MODULE_JSON5)) {
console.log('[SKIP] module.json5 不存在,跳过权限注入')
process.exit(0)
}
const json = JSON.parse(fs.readFileSync(MODULE_JSON5, 'utf8'))
json.module.requestPermissions = json.module.requestPermissions || []
const REQUIRED_PERMISSIONS = [
{ name: 'ohos.permission.VIBRATE' }
]
let changed = false
for (const perm of REQUIRED_PERMISSIONS) {
if (!json.module.requestPermissions.find(p => p.name === perm.name)) {
json.module.requestPermissions.push(perm)
console.log('[OK] 已添加 ' + perm.name)
changed = true
}
}
if (changed) {
fs.writeFileSync(MODULE_JSON5, JSON.stringify(json, null, '\t'))
}
3.2 集成到构建流程
在 dev-harmony.bat 的 npx uni build 之后添加一步:
echo [Step 1.4] 注入鸿蒙权限声明...
node scripts/inject-harmony-permissions.js
if errorlevel 1 (
echo [WARN] 权限注入失败,继续构建...
)
完整构建流程变为:
Step 1: npx uni build -p app-harmony ← 生成 module.json5(只有 INTERNET)
Step 1.4: 注入 VIBRATE 权限 ← 自动编辑 module.json5
Step 1.5: 注入图标
Step 1.6: 设置包名
Step 1.7: 重置签名
Step 2: 注入 local.properties
Step 3: 打开 DevEco Studio → Build → Run
3.3 验证
构建后检查 dist/dev/app-harmony/entry/src/main/module.json5:
{
"module": {
"requestPermissions": [
{ "name": "ohos.permission.INTERNET" },
{ "name": "ohos.permission.VIBRATE" } // ← 注入成功
]
}
}
控制台输出:
[Step 1.4] 注入鸿蒙权限声明...
[OK] 已添加 ohos.permission.VIBRATE
四、失败路径总结
| 尝试 | 方式 | 结果 |
|---|---|---|
| 方案 A | manifest.json 的 app-harmony.distribute.requestPermissions | 无效,构建系统不处理此字段 |
| 方案 B | uni_modules harmony-permissions UTS 插件 | 无效,权限不被合并,还导致 UTS 编译依赖缺失 |
| 方案 C | 构建后 node 脚本直接编辑 module.json5 | 有效 |
五、核心教训
manifest.json的app-harmony.distribute支持字段非常有限(基本只有icons),权限声明不是通过 manifest 配置的。微信小程序能震动 ≠ 系统设置没问题。小程序运行在微信容器里,权限走的是微信的,和独立 App 完全不同。这个对比检验会误导排查方向。
usage: 'touch'理论上不需要权限,但实际可能需要。HarmonyOS 不同 SDK 版本的行为有差异,声明了权限更保险。原生层的错误被吞没。
VibrateNative.ets里startVibration的 Promise 错误只console.error,JS 侧success/fail回调拿不到真实状态。排查时不能只看 JS 回调。当构建系统不支持某个配置时,构建后脚本注入是最可靠的 fallback。
dev-harmony.bat本来就是自定义构建流程,加一步权限注入完全可控。
本文记录了 uni-app 鸿蒙 App 震动功能从失效到修复的完整排查过程,核心发现是鸿蒙权限配置无法通过 manifest.json 生效,需要构建后直接编辑 module.json5。希望对同样遇到鸿蒙权限问题的开发者有所帮助。