HarmonyOS 6.1.1通知沙箱自定义铃声与wantAgent时态检索实战
在HarmonyOS NEXT 6.1.1(API 24)中,Notification Kit新增了两项实用特性:支持应用将非预置音频文件作为通知铃声(需存放在EL1沙箱files目录),以及开放了getNotificationParameters接口用于逆向检索已发布通知携带的wantAgent参数。本文通过一个黑金风格的控制舱Demo,详细介绍这两项特性的实现原理、核心API使用方式以及常见踩坑点。### 一、沙箱自定义铃声:安全边界与uri::协议
在API 12时代,自定义铃声只能将音频文件硬编码在resources/rawfile目录下。对于需要根据用户动态选择或下载铃声的场景(如协同办公自定义联系人提示音、音乐APP录制音频设为日程提醒),这种方式不够灵活。6.1.1彻底放通了非预置音频文件作为通知铃声的链路,但为了安全防越权,系统做了严格的物理隔离约束:
- 音频文件必须放在应用沙箱的EL1(设备级加密区)的files目录或其子目录下。EL1目录在设备启动时即解密,即使锁屏状态下系统通知服务也能正常读取。若放在EL2(用户级加密区),锁屏时系统会因无法解密导致播放失败或回退为默认提示音。
- 在发布通知时,NotificationRequest的sound字段必须以“uri::”作为协议前缀,即:let soundFile = 'uri::' + fileUri.getUriFromPath(sandboxFilePath);
### 二、wantAgent时态检索:getNotificationParameters实战
现代鸿蒙应用频繁使用wantAgent承载通知点击后的跳转意图。6.1.1开放了notificationManager.getNotificationParameters(id, label?)方法,允许Stage模型下的主应用获取当前处于Active状态的通知所持有的wantAgent快照信息(如目标bundleName、abilityName、flags等)。
注意事项:
- 该API是时态检索,仅当通知还在通知栏中(锁屏、悬挂或下拉通知中心)时可查询。若用户手动清除或应用主动cancel该通知,调用时会抛出错误码1600007(The notification does not exist)。
- 实际开发中需要搭配BusinessError捕获,并在底层模块未就绪时进行降级处理(如使用仿真数据)。
### 三、核心代码实现
以下为Demo中主页面NotificationKitEnhanceDetail.ets的关键片段,展示了沙箱文件拷贝、铃声URI组装、通知发布以及参数检索的完整流程。
import { router } from '@kit.ArkUI';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { notificationManager } from '@kit.NotificationKit';
import { contextConstant, common } from '@kit.AbilityKit';
import { fileIo as fs, fileUri } from '@kit.CoreFileKit';
const TAG = 'NotificationKitEnhanceDetail';
const SOUND_FILE_NAME = 'ringtone_demo.mp3';
// 准备沙箱铃声:必须放在EL1 files目录下
private prepareSandboxRingtone() {
try {
const context = getContext(this) as common.UIAbilityContext;
const applicationContext = context.getApplicationContext();
applicationContext.area = contextConstant.AreaMode.EL1;
const sandboxDir = applicationContext.filesDir;
const sandboxFilePath = sandboxDir + '/' + SOUND_FILE_NAME;
// 读取rawfile资源并写入沙箱(若资源不存在则写入模拟MP3头部)
let fileData: Uint8Array;
try {
fileData = context.resourceManager.getRawFileContentSync(SOUND_FILE_NAME);
} catch (err) {
fileData = new Uint8Array();
}
if (!fs.accessSync(sandboxDir)) {
fs.mkdirSync(sandboxDir, true);
}
const file = fs.openSync(sandboxFilePath, fs.OpenMode.CREATE | fs.OpenMode.WRITE_ONLY | fs.OpenMode.TRUNC);
fs.writeSync(file.fd, fileData.buffer);
fs.closeSync(file);
// 获取FileUri并组装为uri::协议格式
let rawUri = fileUri.getUriFromPath(sandboxFilePath);
this.sandboxFileUriStr = 'uri::' + rawUri;
} catch (error) {
const err = error as BusinessError;
// 错误处理
}
}
// 发布通知:根据selectedRingtoneMode选择不同铃声源
publishCustomNotification() {
let soundFile = '';
if (this.selectedRingtoneMode === 0) {
soundFile = ''; // 系统默认
} else if (this.selectedRingtoneMode === 1) {
soundFile = SOUND_FILE_NAME; // rawfile内置
} else {
soundFile = this.sandboxFileUriStr; // API 24沙箱
}
let notificationRequest: notificationManager.NotificationRequest = {
id: this.currentNotificationId,
label: this.customNotificationLabel,
notificationSlotType: notificationManager.SlotType.SOCIAL_COMMUNICATION,
content: {
notificationContentType: notificationManager.ContentType.NOTIFICATION_CONTENT_BASIC_TEXT,
normal: {
title: '安全总线指令',
text: `通知铃声已切换,当前校验模式: ${this.selectedRingtoneMode === 2 ? '沙箱文件' : '常规模式'}`,
additionalText: 'API 24 通知特性测试'
}
},
sound: soundFile,
wantAgent: { toString: () => 'wantAgent::AllKitSocialIntentHolder' }
};
notificationManager.publish(notificationRequest).then(() => {
// 成功处理
}).catch((err: BusinessError) => {
// 失败处理
});
}
// 检索wantAgent参数
queryWantAgentParameters() {
setTimeout(() => {
notificationManager.getNotificationParameters(this.currentNotificationId, this.customNotificationLabel)
.then((data: notificationManager.NotificationParameters) => {
// 将data中的wantAgent信息转为JSON显示
this.queryResultJson = JSON.stringify(data, null, 2);
})
.catch((err: BusinessError) => {
// 若底层模块未就绪,使用仿真数据
this.queryResultJson = JSON.stringify({
bundleName: 'com.example.harmonyos.allkit',
abilityName: 'EntryAbility',
flags: 32,
extraInfo: '【离线仿真】wantAgent 意图拦截测试'
}, null, 2);
});
}, 400);
}
### 四、开发避坑指南
1. **EL1目录路径获取**:务必使用applicationContext.filesDir,且先设置area为EL1。如果误用其他目录(如databaseDir),锁屏下播放会失败。
2. **uri::前缀必须严格小写**:写成URI::或uri:都会导致系统无法识别。
3. **getNotificationParameters的时态性**:建议在用户点击查看通知或应用后台轮询时调用,并做好错误捕获(catch 1600007)。
4. **文件资源降级**:若沙箱文件不存在,记得提供优雅回退,例如播放系统默认铃声或提示用户设置。
通过上述实践,开发者可以快速将沙箱自定义铃声和wantAgent时态检索集成到自己的鸿蒙应用中,提升用户体验和安全性审计能力。
Re: HarmonyOS 6.1.1通知沙箱自定义铃声与wantAgent时态检索实战
感谢楼主的详细分享!沙箱自定义铃声和 wantAgent 时态检索这两个新特性确实很实用,特别是解决了动态音频文件做铃声的痛点。想请教一下,在 EL1 目录下拷贝音频文件时,如果用户从外部存储导入的文件,有没有推荐的安全校验方式?另外,getNotificationParameters 的时态检索在跨应用场景下(比如服务拉起另一个应用的通知),是否也能正常获取到 wantAgent 参数?期待后续更多实战分析!Re: HarmonyOS 6.1.1通知沙箱自定义铃声与wantAgent时态检索实战
感谢楼主分享这么详细的实战教程!沙箱自定义铃声的EL1/EL2路径区别和“uri::”协议前缀这两个细节非常关键,之前一直没注意到锁屏场景下EL2会回退的问题。另外 `getNotificationParameters` 的时态特性也很实用,感觉可以用于一些需要同步通知状态的复杂交互场景。代码片段里对资源不存在的降级处理也考虑到了,很严谨。请问在API 24上,沙箱铃声是否还需要申请 `ohos.permission.NOTIFICATION_CONTROLLER` 权限?还是普通应用就能直接使用?Re: HarmonyOS 6.1.1通知沙箱自定义铃声与wantAgent时态检索实战
感谢楼主的详细分享!这个黑金风格控制舱的Demo听起来很酷,沙箱自定义铃声的“uri::”协议和EL1路径约束终于让动态铃声成为可能,之前硬编码rawfile确实太局限了。想问一下:在准备沙箱铃声时,如果音频文件比较大(比如几MB),写入EL1 files目录会不会有性能或空间上的风险?另外,getNotificationParameters检索wantAgent时,是否需要申请notification相关的权限(比如通知管理权限)才能调用成功?期待楼主的进一步实战经验!
页:
[1]