
描述:语音记录功能需要长时间录音;退到后台后若被系统回收会造成文件损坏。想要通过前台服务 + 常驻通知保活,并在页面前后台切换、来电打断等情况下正确暂停/恢复录音。有没有较稳妥的状态机与生命周期协同实践?
相关代码:
// 伪代码:下单成功后拿到 purchaseToken 传给服务端二次校验
const order = await iap.purchase('sub_premium_month');
await http.request('/verify', { method: 'POST', data: { token: order.purchaseToken } });
preferences.getPreferences({ name: 'iap' }).then(p => p.put('active', true));

1. 整体架构
前台服务:使用Service Ability作为录音服务,通过keepBackgroundRunning()设置为前台服务,并显示常驻通知。
状态机:设计一个录音状态机来管理录音的生命周期(如空闲、录音中、暂停、停止),确保状态转换的一致性。
生命周期监听:监听应用的前后台切换和来电事件,及时暂停和恢复录音。
文件处理:使用临时文件录音,并在最终ize时确保文件完整,避免损坏。
2. 状态机设计
定义录音状态机,包含以下状态和转换:
状态:
IDLE:初始状态,录音未开始。
RECORDING:正在录音。
PAUSED:录音暂停。
STOPPED:录音停止,文件已保存。
转换方法:
startRecording():从IDLE或PAUSED切换到RECORDING。
pauseRecording():从RECORDING切换到PAUSED。
resumeRecording():从PAUSED切换到RECORDING。
stopRecording():从RECORDING或PAUSED切换到STOPPED。
3. 实现步骤
3.1 创建前台服务(Service Ability)
在ets目录下创建录音服务RecordingService.ts,实现前台服务和录音逻辑。
import featureAbility from '@ohos.ability.featureAbility';
import notification from '@ohos.notification';
import media from '@ohos.multimedia.media';
import fileio from '@ohos.fileio';
export default class RecordingService {
private state: string = 'IDLE';
private mediaRecorder: media.MediaRecorder | null = null;
private outputPath: string = 'path/to/recording/file.mp3'; // 设置录音文件路径
// 启动服务
onStart(want, startId) {
this.setupForegroundService();
this.startRecording();
}
// 停止服务
onStop() {
this.stopRecording();
this.cancelForegroundService();
}
// 设置前台服务
private setupForegroundService() {
let notificationRequest: notification.NotificationRequest = {
content: {
contentType: notification.ContentType.NOTIFICATION_CONTENT_TYPE_TEXT,
normal: {
title: '录音中',
text: '正在后台录音',
additionalText: '点击返回应用'
}
},
id: 1,
isOngoing: true // 常驻通知
};
featureAbility.keepBackgroundRunning(this.context, notificationRequest).then(() => {
console.log('keepBackgroundRunning success');
}).catch((error) => {
console.error('keepBackgroundRunning failed: ' + error);
});
}
// 取消前台服务
private cancelForegroundService() {
featureAbility.cancelBackgroundRunning().then(() => {
console.log('cancelBackgroundRunning success');
}).catch((error) => {
console.error('cancelBackgroundRunning failed: ' + error);
});
}
// 开始录音
private startRecording() {
if (this.state === 'IDLE' || this.state === 'PAUSED') {
if (this.state === 'IDLE') {
this.mediaRecorder = media.createMediaRecorder();
let config: media.RecorderConfig = {
audioSource: media.AudioSource.AUDIO_SOURCE_MIC,
outputFormat: media.OutputFormat.OUTPUT_FORMAT_MPEG_4,
audioEncoder: media.AudioEncoder.AUDIO_ENCODER_AAC,
outputPath: this.outputPath
};
this.mediaRecorder.prepare(config);
}
this.mediaRecorder.start().then(() => {
this.state = 'RECORDING';
console.log('Recording started');
}).catch((error) => {
console.error('Recording start failed: ' + error);
});
}
}
// 暂停录音
private pauseRecording() {
if (this.state === 'RECORDING' && this.mediaRecorder) {
this.mediaRecorder.pause().then(() => {
this.state = 'PAUSED';
console.log('Recording paused');
}).catch((error) => {
console.error('Recording pause failed: ' + error);
});
}
}
// 恢复录音
private resumeRecording() {
if (this.state === 'PAUSED' && this.mediaRecorder) {
this.mediaRecorder.resume().then(() => {
this.state = 'RECORDING';
console.log('Recording resumed');
}).catch((error) => {
console.error('Recording resume failed: ' + error);
});
}
}
// 停止录音
private stopRecording() {
if (this.state === 'RECORDING' || this.state === 'PAUSED') {
if (this.mediaRecorder) {
this.mediaRecorder.stop().then(() => {
this.mediaRecorder.release();
this.mediaRecorder = null;
this.state = 'STOPPED';
console.log('Recording stopped');
}).catch((error) => {
console.error('Recording stop failed: ' + error);
});
}
}
}
}
3.2 监听应用生命周期事件
在应用主页面或全局环境中,监听应用前后台切换事件,并控制录音服务的暂停和恢复。
import app from '@app.app';
import { AppStateObserver } from '@app.app';
// 注册应用状态观察者
app.registerAppStateObserver({
onAppStateChanged: (newState) => {
if (newState === 'background') {
// 应用进入后台,暂停录音
this.pauseRecording();
} else if (newState === 'foreground') {
// 应用回到前台,恢复录音
this.resumeRecording();
}
}
});
3.3 监听来电事件
使用电话状态监听器,在来电时暂停录音,挂断后恢复。
import observer from '@ohos.telephony.observer';
// 订阅电话状态变化
observer.on('callStateChange', (data) => {
if (data.state === observer.CallState.CALL_STATE_OFFHOOK) {
// 电话接通,暂停录音
this.pauseRecording();
} else if (data.state === observer.CallState.CALL_STATE_IDLE) {
// 电话挂断,恢复录音
this.resumeRecording();
}
});
3.4 启动和停止服务
在需要开始录音的页面中,启动前台服务;在结束录音时停止服务。
import featureAbility from '@ohos.ability.featureAbility';
// 启动录音服务
let want = {
bundleName: 'com.example.recordingapp',
abilityName: 'RecordingService'
};
featureAbility.startAbility(want).then(() => {
console.log('Service started');
}).catch((error) => {
console.error('Service start failed: ' + error);
});
// 停止录音服务
featureAbility.stopAbility(want).then(() => {
console.log('Service stopped');
}).catch((error) => {
console.error('Service stop failed: ' + error);
});
