鸿蒙6:面试录音解决方案(上)
本文介绍了鸿蒙系统中应用权限管理和录音功能实现的相关知识。首先详细讲解了应用权限的分类(system_grant和user_grant)及申请流程,包括敏感权限的特殊处理方式。其次,通过具体代码示例展示了如何封装权限工具类,以及在录音页面实现权限请求和授权逻辑。最后,重点介绍了使用AvRecorder实现音频录制、动态声音波形显示,以及利用AvPlayer播放音频文件并显示进度条的具体实现方法。文
注意:博主有个鸿蒙专栏,里面从上到下有关于鸿蒙next的教学文档,大家感兴趣可以学习下
如果大家觉得博主文章写的好的话,可以点下关注,博主会一直更新鸿蒙next相关知识
目录
1. 面试录音

1.1. 应用权限
1.1.1. 应用权限概述
系统提供了一种允许应用访问系统资源(如:通讯录等)和系统能力(如:访问摄像头、麦克风等)的通用权限访问方式,来保护系统数据(包括用户个人数据)或功能,避免它们被不当或恶意使用。
应用申请敏感权限时,必须填写权限使用理由字段,敏感权限通常是指与用户隐私密切相关的权限,包括地理位置、相机、麦克风、日历、健身运动、身体传感器、音乐、文件、图片视频等权限。参考向用户申请授权。
- system_grant
在配置文件中,声明应用需要请求的权限后,系统会在安装应用时自动为其进行权限预授予,开发者不需要做其他操作即可使用权限。
- user_grant
-
- 在配置文件中,声明应用需要请求的权限,且要设置需要使用的场景+使用原因
- 调用 requestPermissionsFromUser() 方法后,应用程序将等待用户授权的结果。如果用户授权,则可以继续访问目标操作。如果用户拒绝授权,则需要提示用户必须授权才能访问当前页面的功能,并引导用户到系统应用“设置”中打开相应的权限。可参考二次向用户申请权限 requestPermissionOnSetting() 。
录音授权演示:
module.json5
"requestPermissions": [
{
"name": 'ohos.permission.INTERNET'
},
{
"name": "ohos.permission.MICROPHONE",
"reason": "$string:permission_microphone",
"usedScene": {
"abilities": ["EntryAbility"]
}
}
],
原因格式:用于xxx模块xxx功能
{
"string": [
...
{
"name": "permission_microphone",
"value": "录音功能申请麦克风权限"
}
]
}
页面测试:
import { abilityAccessCtrl, Permissions } from "@kit.AbilityKit"
import { HcNavBar } from "../../commons/components/HcNavBar"
@Component
struct AudioPage {
aboutToAppear(): void {
this.requestPermission()
}
async requestPermission() {
// 1. 用户授权
const permissionList: Permissions[] = ['ohos.permission.MICROPHONE']
const atManager = abilityAccessCtrl.createAtManager()
const ctx = getContext(this)
const result = await atManager.requestPermissionsFromUser(ctx, permissionList)
const flag = result.authResults.every(item => item === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
// 2. 二次授权
if (!flag) {
const result2 = await atManager.requestPermissionOnSetting(ctx, permissionList)
const flag2 = result2.every(item => item === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
console.log(flag2.toString())
}
}
build() {
//必须用NavDestination包裹
NavDestination() {
HcNavBar({ title: '录音功能' })
}
.hideTitleBar(true)
}
}
// 跳转页面入口函数
@Builder
export function AudioBuilder() {
AudioPage()
}
配置路由
{
"routerMap": [
...
{
"name": "AudioPage",
"pageSourceFile": "src/main/ets/pages/Audio/AudioPage.ets",
"buildFunction": "AudioBuilder"
}
]
}
跳转页面
this.toolsBuilder({
icon: $r('app.media.ic_mine_invite'),
name: '面试录音',
onClick: () => {
auth.checkAuth({
name: 'AudioPage'
})
}
})
1.1.2. permission 工具
目标:封装权限工具,提供请求用户权限,拉起用户权限设置的能力
import { abilityAccessCtrl, Permissions } from '@kit.AbilityKit';
import { AppStorageV2 } from '@kit.ArkUI';
import { SavedContext } from '../../models';
class Permission {
// 请求用户授权
async requestPermissions(permissions: Permissions[]) {
const atManager = abilityAccessCtrl.createAtManager()
const ctx = AppStorageV2.connect(SavedContext)!.context
if (ctx) {
const result = await atManager.requestPermissionsFromUser(ctx, permissions)
return result.authResults.every(result => result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
}
return false
}
// 打开权限设置 beta3
async openPermissionSetting(permissions: Permissions[]) {
const atManager = abilityAccessCtrl.createAtManager()
const ctx = AppStorageV2.connect(SavedContext)!.context
if (ctx) {
const authResults = await atManager.requestPermissionOnSetting(ctx, permissions)
return authResults.every(result => result === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
}
return false
}
}
export const permission = new Permission()
1.1.3. 录音授权
目标:使用权限请求工具,在录音页面实现请求权限,无权限不可进入



import { HcNavBar } from "../../commons/components/HcNavBar"
import { promptAction } from "@kit.ArkUI"
import { navPathStack } from "../Index"
import { permission } from "../../commons/utils/Permission"
import { Permissions } from "@kit.AbilityKit"
@ComponentV2
struct AudioPage {
permissions: Permissions[] = ['ohos.permission.MICROPHONE']
confirmConfig: promptAction.ShowDialogOptions = {
title: "温馨提示",
message: "未授权使用麦克风将无法使用该面试录音功能,是否前往设置进行授权?",
buttons: [
{ text: '离开', color: $r('app.color.common_gray_01') },
{ text: '去授权', color: $r('app.color.black') }
]
}
aboutToAppear(): void {
this.requestPermission()
}
async requestPermission() {
try {
// 第一请求授权
const isOk = await permission.requestPermissions(this.permissions)
if (isOk) {
return
}
// 弹窗提示
const confirm = await promptAction.showDialog(this.confirmConfig)
if (confirm.index === 1) {
const isOk2 = await permission.openPermissionSetting(this.permissions)
if (isOk2) {
return
}
}
navPathStack.pop()
} catch (e) {
promptAction.showToast({ message: '未授权' })
navPathStack.pop()
}
}
build() {
//必须用NavDestination包裹
NavDestination() {
HcNavBar({ title: '录音功能' })
}
.hideTitleBar(true)
}
}
// 跳转页面入口函数
@Builder
export function AudioBuilder() {
AudioPage()
}
1.2. 录音知识
1.2.1. 使用 AvRecorder 录音
目标:使用 AvRecorder 实现音频录制存储到应用沙箱

实现步骤:
- 需要一个文件接收音频数据
- 准备录音配置
- 使用 AvRecorder 实现开始录音,结束录音
落地代码:
avRecorder?: media.AVRecorder // 音视频录制管理类
fd?: number // 资源句柄(fd)
filePath?: string // 文件路径
async startRecord() {
// 1. 准备一个文件接收录音
const ctx = getContext(this)
const filePath = ctx.filesDir + '/' + Date.now() + '.m4a'
this.filePath = filePath
const file = fileIo.openSync(filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
this.fd = file.fd
// 2. 准备路由配置对象
const config: media.AVRecorderConfig = {
audioSourceType: media.AudioSourceType.AUDIO_SOURCE_TYPE_MIC,
profile: {
audioBitrate: 100000, // 音频比特率
audioChannels: 1, // 音频声道数
audioCodec: media.CodecMimeType.AUDIO_AAC, // 音频编码格式,当前只支持aac
audioSampleRate: 48000, // 音频采样率
fileFormat: media.ContainerFormatType.CFT_MPEG_4A, // 封装格式,当前只支持m4a
},
url: `fd://${file.fd}`
}
// 3. 开始录制
const avRecorder = await media.createAVRecorder()
await avRecorder.prepare(config)
await avRecorder.start()
this.avRecorder = avRecorder
}
async stopRecord() {
if (this.avRecorder) {
await this.avRecorder.stop()
await this.avRecorder.release()
fileIo.closeSync(this.fd)
}
}
//必须用NavDestination包裹
NavDestination() {
HcNavBar({ title: '录音功能' })
Button('开始录音')
.onClick(() => {
this.startRecord()
})
Button('结束录音')
.onClick(() => {
this.stopRecord()
})
}
.hideTitleBar(true)
1.2.2. 录音声音振动效果
目标:根据声音的大小实现声音振动特效

实现步骤:
- 通过 getAudioCapturerMaxAmplitude 观察音频区间
- 封装振动组件,通过声音振幅数据实现振动效果
落地代码:
1)获取振幅数据,出入振动组件 AudioPage.ets
timer?: number
@Local maxAmplitude: number = 0
// 每100ms获取一下声音振幅
this.timer = setInterval(async () => {
this.maxAmplitude = await avRecorder.getAudioCapturerMaxAmplitude()
logger.debug('startRecord', this.maxAmplitude.toString())
}, 100)
async stopRecord() {
if (this.avRecorder) {
await this.avRecorder.stop()
await this.avRecorder.release()
fileIo.closeSync(this.fd)
// stopRecord 清理定时器
clearInterval(this.timer)
}
}
AudioBoComp({ maxAmplitude: this.maxAmplitude })
2)实现振动组件 Audio/components/AudioBoComp.ets
@ComponentV2
export struct AudioBoComp {
@Local per: number = 0
@Param maxAmplitude: number = 0
@Monitor('maxAmplitude')
onChange() {
animateTo({ duration: 100 }, () => {
if (this.maxAmplitude < 500) {
this.per = 0
} else if (this.maxAmplitude > 30000) {
this.per = 1
} else {
this.per = this.maxAmplitude / 30000
}
})
}
build() {
Row({ space: 5 }) {
ForEach(Array.from({ length: 30 }), () => {
Column()
.layoutWeight(1)
.height(this.per * 100 * Math.random())
.backgroundColor($r('app.color.common_blue'))
})
}
.width('100%')
.height(100)
}
}
1.2.3. 使用 AvPlayer 播放
目标:能够使用 AvPlayer 播放应用沙箱中的音频文件,且显示进度条
落地代码:
avPlayer?: media.AVPlayer
@Local total: number = 0
@Local value: number = 0
async startPlay() {
try {
const file = fileIo.openSync(this.filePath, fileIo.OpenMode.READ_ONLY)
const avPlayer = await media.createAVPlayer()
avPlayer.on('stateChange', state => {
if (state === 'initialized') {
avPlayer.prepare()
} else if ( state === 'prepared') {
avPlayer.loop = true
this.total = avPlayer.duration
avPlayer.play()
}
})
// 当前播放时间改变
avPlayer.on('timeUpdate', (time) => {
this.value = time
})
avPlayer.url = `fd://${file.fd}`
this.avPlayer = avPlayer
} catch (e) {
logger.error('startPlay', JSON.stringify(e))
}
}
stopPlay() {
if (this.avPlayer) {
this.avPlayer.stop()
this.avPlayer.release()
}
}
Button('开始播放')
.onClick(() => {
this.startPlay()
})
Button('停止播放')
.onClick(() => {
this.stopPlay()
})
Progress({ total: this.total, value: this.value })
.width('100%')
HarmonyOS赋能资源丰富度建设(第四期)-吴东林
更多推荐




所有评论(0)