一、权限相关信息
以上是官网的权限列表,权限也分等级,不同的等级会有不同的授权方式, 比如:
第一个是禁默授权,第三个我们调不了,今天我们要做的是录音权限属于第二个,一套代码搞定所有权限控制!
二、提前配置好需要调用的权限
和我们调用网络权限一样,放在module.json5的requestPermissions里
比如有需要做定位权限的,就可以这样配置(权限等级为normal的只用配置name):
// 位置信息
{ // 模糊定位
"name": "ohos.permission.APPROXIMATELY_LOCATION",
"reason": '$string:permission_reason_location',
"usedScene": {} },
{ // 精准定位
"name": "ohos.permission.LOCATION",
"reason": '$string:permission_reason_location',
"usedScene": {} },
然后在string.json里面配置对应的value{ "name": "permission_reason_camera", "value": "相机授权原因" },
- 细节:这里获得是WGS-84的坐标系,在国内显示的是国测局的坐标系(地图上)
地图需要多配置一个AGC平台上获取的id,就是搞四个证书的地方
接下来继续搞我们的录音
三、写一个类用于权限的管理(三个功能)
1.功能一:检查是否授权
checkPermissions(permissions: Permissions[]) {
// 1. 创建应用权限管理器
const atManager = abilityAccessCtrl.createAtManager()
// 2. 获取 bundle 包信息,Sync 写法
const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
// 3. 提取 tokenID
const tokenID = bundleInfo.appInfo.accessTokenId
// 4. 检测是否授权,Sync 写法,升级成遍历权限组(数组)!!!
const grantStatus = permissions.map(item => atManager.checkAccessTokenSync(tokenID, item))
// 返回权限数组的检测结果
return grantStatus.every(v => v === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
// 温馨提示:当前函数没有用到 Promise,都是用 Sync 函数,好处:可直接再组件渲染权限检测结果
}
2.功能二:动态申请授权(首次弹窗申请)
async requestPermissions(permissions: Permissions[]) {
// 1. 创建应用权限管理器
const atManager = abilityAccessCtrl.createAtManager()
// 2. 向用户申请 user_grant 权限(温馨提示:首次申请时会弹窗,后续申请则不会再出现弹窗)
const requestResult = await atManager.requestPermissionsFromUser(
getContext(), // 应用上下文
permissions // 参数:权限列表(数组)
)
// 通过 every 检查权限是否都成功授权
const isAuth = requestResult.authResults.every(item => item === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
// Promise.resolve() 返回 Promise 成功,await 后续代码,正常执行
// Promise.reject() 返回 Promise 错误,await 后续代码,不被执行,Promise.reject() 的结果可被 catch 捕获
return isAuth === true ? Promise.resolve(true) : Promise.reject(false)
}
3.功能三:打开系统设置的权限管理页(处理授权结果)
openPermissionSettingsPage() {
// 1. 获取应用上下文,并通过 as 断言收窄类型为 UIAbilityContext,否则 context 默认类型无法调用 startAbility 方法
const context = getContext() as common.UIAbilityContext
// 2. 获取 bundle 包信息
const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION)
// 3. 通过 startAbility 打开 系统设置 页
context.startAbility({
bundleName: 'com.huawei.hmos.settings', // 固定写法CV:设置页的包名
abilityName: 'com.huawei.hmos.settings.MainAbility', // 固定写法CV:设置页的 ability 名
uri: 'application_info_entry', // 固定写法CV:打开 设置->应用和元服务
parameters: {
// 打开指定应用(包)的详情页面
// pushParams: 'com.itheima.hm_guardian'
// 应用包名可通过 bundleManager 动态获取
pushParams: bundleInfo.name
}
})
}
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';最后new一下导出去就可以了,这套代码可以供所有的调用权限使用
四、开始做录音和播放的功能
1.核心:创建音频采集器+创建音频播放器
audio.createAudioCapturer(this.audioCapturerOptions)
这里的audioCapturerOptions包含音频流信息和音频渲染器信息,这里代码固定,除非要做音频改动的
// 音频流信息
audioStreamInfo: audio.AudioStreamInfo = {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000, // 采样率
channels: audio.AudioChannel.CHANNEL_2, // 通道
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE, // 采样格式
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW // 编码格式
};
// 音频采集器信息
audioCapturerInfo: audio.AudioCapturerInfo = {
source: audio.SourceType.SOURCE_TYPE_MIC, // 声音来源
capturerFlags: 0, // 0 代表普通音频采集器,1 代表低时延音频采集器 (ArkTS接口暂不支持低时延音频采集器)
};
// createAudioCapturer 创建音频采集器时的必传参数
audioCapturerOptions: audio.AudioCapturerOptions = {
streamInfo: this.audioStreamInfo, // 音频流信息
capturerInfo: this.audioCapturerInfo, // 音频采集器信息
};
audio.createAudioRenderer(this.audioRendererOptions)这个播放器,如果要那种根据耳朵贴近来自动切换听筒模式,需要用到传感器权限,也是用上面的代码直接搞定!
// 音频渲染器信息
audioRendererInfo: audio.AudioRendererInfo = {
// usage: audio.StreamUsage.STREAM_USAGE_VOICE_COMMUNICATION, // 用听筒播放
usage: audio.StreamUsage.STREAM_USAGE_MOVIE, // 用外放喇叭播放
// usage: audio.StreamUsage.STREAM_USAGE_MUSIC, // 用外放喇叭播放
// usage: audio.StreamUsage.STREAM_USAGE_GAME, // 用外放喇叭播放
rendererFlags: 0
};
// 音频渲染器配置
audioRendererOptions: audio.AudioRendererOptions = {
streamInfo: this.audioStreamInfo, // 音频流信息
rendererInfo: this.audioRendererInfo // 音频渲染器信息
};
2.做录音功能
-
1.先申请权限,用上面的new出来的对象
.requestPermissions(['ohos.permission.MICROPHONE'])这里返回的是个布尔类型,需要用户开放权限,或者给他直接跳到设置页面让他勾! -
2.创建音频采集器
-
3.
getContext()获得应用上下文 -
4.调用
fileIo里面的open方法创建一个文件路径用来储存我们的录音(路径,可读可写) -
5.采集器数据读入
on方法 -
6.采集器开始录制
start方法
// 1. 创建音频采集器
const audioCapturer = await audio.createAudioCapturer(this.audioCapturerOptions);
// 保存起来,方便其他地方使用
this.audioCapturer = audioCapturer
this.isCreate = true
// ------ 文件系统 fileIo ------
// 1) 创建并打开本地文件
const context = getContext()
// 2) 通过应用上下文,获取到应用的 files 路径
this.filePath = context.filesDir + '/' + Date.now() + '.wav'
// 3) 创建并打开文件,注意打开模式选择可读可写 READ_WRITE
const file = fileIo.openSync(this.filePath, fileIo.OpenMode.CREATE | fileIo.OpenMode.READ_WRITE)
// ------ 文件系统 ------
// 2. 订阅音频数据读入
audioCapturer.on('readData', (buffer) => {
// 4) 把 buffer 写入到打开的文件中(file.fd为打开文件后标识)
fileIo.writeSync(file.fd, buffer)
console.log('读取采集器的音频流大小(单位B)', buffer.byteLength);
})
// // 2.x 订阅采集器状态
audioCapturer.on('stateChange', (state) => {
this.audioState = state
})
// 3. 开始录制音频
await audioCapturer.start()
// 如果以上步骤出错,就不会运行到这里
promptAction.showToast({ message: '音频采集器创建成功' })
3.做录音的暂停、继续、释放资源功能
if (this.audioCapturer?.state === audio.AudioState.STATE_RUNNING) {
// 停止采集
await this.audioCapturer?.stop()
promptAction.showToast({ message: '停止成功' })
}
// 开始(继续)采集
await this.audioCapturer?.start()
promptAction.showToast({ message: '继续开始' })
// 释放资源(释放内存和硬件)
await this.audioCapturer?.release()
this.isCreate = false
promptAction.showToast({ message: '释放资源' })
4.做播放录音功能
- 1.创建音频渲染器(播放器)
- 2.根据路径打开文件(这里要传文件路径的参数,点谁就是谁呗)
- 3.使用渲染器的
on方法读取文件的buffer,写入到渲染器中 - 4.开始渲染
start方法 - 5.根据文件的大小,设置自动停止渲染(不然会不停的播放最后的buffer)
// 1. 创建音频渲染器
const audioRenderer = await audio.createAudioRenderer(this.audioRendererOptions);
// 保存起来
this.audioRenderer = audioRenderer
// 根据路径打开文件
const file = fileIo.openSync(this.filePath)
// 获取文件信息(大小,创建时间等)
const fileStat = fileIo.statSync(file.fd)
console.log('音频渲染器 buffer 当前文件大小为(单位B)', fileStat.size)
// 准备一个累加值,用于自动停止渲染
let bufferSize = 0
// 2. 订阅(buffer写入数据到音频渲染器中,就能发出声音)
audioRenderer.on('writeData', (buffer) => {
// 读取打开文件的 buffer,写入到渲染器中,就能发出声音
fileIo.readSync(file.fd, buffer)
// buffer 大小累加,用于自动停止
bufferSize += buffer.byteLength
// 累加的结果,是否已超过文件大小
if (bufferSize >= fileStat.size) {
// 自动停止渲染
audioRenderer.stop()
}
console.log('音频渲染器 buffer 累加结果(单位B)', bufferSize)
})
// 3. 开始渲染
audioRenderer.start()
promptAction.showToast({ message: '音频渲染器正常' })
5.做渲染的停止和释放资源功能(一样的)
await this.audioRenderer?.stop()
promptAction.showToast({ message: '停止成功' })
await this.audioRenderer?.release()
promptAction.showToast({ message: '销毁实例,释放资源成功' }