
鸿蒙跨设备录音机应用设计与实现 原创
鸿蒙跨设备录音机应用设计与实现
一、系统架构设计
基于HarmonyOS的音频录制能力和分布式技术,我们设计了一个跨设备同步的录音机应用,能够录制音频文件并在多设备间同步录音记录。
!https://example.com/audio-recorder-arch.png
系统包含三个核心模块:
音频录制模块 - 使用@ohos.multimedia.audio实现音频录制
分布式同步模块 - 通过@ohos.distributedData实现多设备录音记录同步
文件存储模块 - 使用@ohos.fileio管理录音文件
二、核心代码实现
录音服务(ArkTS)
// AudioRecorderService.ets
import audio from ‘@ohos.multimedia.audio’;
import fileIO from ‘@ohos.fileio’;
import distributedData from ‘@ohos.distributedData’;
const RECORD_SYNC_CHANNEL = ‘audio_records_sync’;
class AudioRecorderService {
private static instance: AudioRecorderService = null;
private audioRecorder: audio.AudioRecorder;
private dataManager: distributedData.DataManager;
private currentRecordPath: string = ‘’;
private listeners: RecorderListener[] = [];
private constructor() {
this.initDataManager();
public static getInstance(): AudioRecorderService {
if (!AudioRecorderService.instance) {
AudioRecorderService.instance = new AudioRecorderService();
return AudioRecorderService.instance;
private initDataManager() {
this.dataManager = distributedData.createDataManager({
bundleName: 'com.example.audiorecorder',
area: distributedData.Area.GLOBAL
});
this.dataManager.registerDataListener(RECORD_SYNC_CHANNEL, (data) => {
this.handleSyncData(data);
});
public async startRecording(): Promise<boolean> {
try {
// 创建录音实例
const audioStreamInfo: audio.AudioStreamInfo = {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
};
const audioRecorderConfig: audio.AudioRecorderConfig = {
audioStreamInfo: audioStreamInfo,
filePath: this.getNewRecordPath()
};
this.audioRecorder = await audio.createAudioRecorder();
await this.audioRecorder.prepare(audioRecorderConfig);
await this.audioRecorder.start();
this.currentRecordPath = audioRecorderConfig.filePath;
return true;
catch (err) {
console.error('开始录音失败:', JSON.stringify(err));
return false;
}
public async stopRecording(): Promise<AudioRecord | null> {
if (!this.audioRecorder) return null;
try {
await this.audioRecorder.stop();
await this.audioRecorder.release();
const recordInfo: AudioRecord = {
id: Date.now().toString(),
filePath: this.currentRecordPath,
fileName: this.getFileNameFromPath(this.currentRecordPath),
duration: await this.getAudioDuration(this.currentRecordPath),
createTime: Date.now(),
deviceId: this.getDeviceId()
};
this.currentRecordPath = '';
this.syncRecord(recordInfo);
return recordInfo;
catch (err) {
console.error('停止录音失败:', JSON.stringify(err));
return null;
}
public async getRecords(): Promise<AudioRecord[]> {
// 实际实现需要从数据库或文件系统中获取
return [];
public async deleteRecord(recordId: string): Promise<boolean> {
// 实际实现需要删除文件和记录
this.syncDelete(recordId);
return true;
public addListener(listener: RecorderListener): void {
if (!this.listeners.includes(listener)) {
this.listeners.push(listener);
}
public removeListener(listener: RecorderListener): void {
this.listeners = this.listeners.filter(l => l !== listener);
private getNewRecordPath(): string {
const dirPath = this.getRecordsDir();
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
return {dirPath}/recording_{timestamp}.wav;
private getRecordsDir(): string {
// 实际实现需要获取应用文件目录
return '/data/storage/el2/base/files/records';
private getFileNameFromPath(path: string): string {
return path.split('/').pop() || 'recording.wav';
private async getAudioDuration(filePath: string): Promise<number> {
// 简化实现,实际需要解析音频文件
return 0;
private syncRecord(record: AudioRecord): void {
this.dataManager.syncData(RECORD_SYNC_CHANNEL, {
type: 'addRecord',
record: record
});
private syncDelete(recordId: string): void {
this.dataManager.syncData(RECORD_SYNC_CHANNEL, {
type: 'deleteRecord',
recordId: recordId
});
private handleSyncData(data: any): void {
if (!data) return;
switch (data.type) {
case 'addRecord':
if (data.record && data.record.deviceId !== this.getDeviceId()) {
this.notifyListeners('add', data.record);
break;
case 'deleteRecord':
if (data.recordId) {
this.notifyListeners('delete', data.recordId);
break;
}
private notifyListeners(type: ‘add’ | ‘delete’, data: any): void {
this.listeners.forEach(listener => {
if (type === ‘add’) {
listener.onRecordAdded(data as AudioRecord);
else {
listener.onRecordDeleted(data as string);
});
private getDeviceId(): string {
// 实际实现需要获取设备ID
return 'local_device';
}
interface AudioRecord {
id: string;
filePath: string;
fileName: string;
duration: number; // 毫秒
createTime: number;
deviceId: string;
interface RecorderListener {
onRecordAdded(record: AudioRecord): void;
onRecordDeleted(recordId: string): void;
export const recorderService = AudioRecorderService.getInstance();
录音机主界面(ArkUI)
// AudioRecorderApp.ets
import { recorderService } from ‘./AudioRecorderService’;
@Entry
@Component
struct AudioRecorderApp {
@State isRecording: boolean = false;
@State records: AudioRecord[] = [];
@State showPlayer: boolean = false;
@State currentRecord: AudioRecord | null = null;
private recorderListener: RecorderListener = {
onRecordAdded: (record) => {
this.records = [record, …this.records];
},
onRecordDeleted: (recordId) => {
this.records = this.records.filter(r => r.id !== recordId);
};
aboutToAppear() {
recorderService.addListener(this.recorderListener);
this.loadRecords();
aboutToDisappear() {
recorderService.removeListener(this.recorderListener);
build() {
Column() {
// 录音控制按钮
this.buildRecordControls()
// 录音列表
List({ space: 10 }) {
ForEach(this.records, (record) => {
ListItem() {
this.buildRecordItem(record)
})
.layoutWeight(1)
// 音频播放器
if (this.showPlayer && this.currentRecord) {
this.buildAudioPlayer()
}
.padding(20)
@Builder
buildRecordControls() {
Row() {
Button(this.isRecording ? ‘停止录音’ : ‘开始录音’)
.onClick(async () => {
if (this.isRecording) {
await this.stopRecording();
else {
await this.startRecording();
})
.width('60%')
.margin({ bottom: 20 })
@Builder
buildRecordItem(record: AudioRecord) {
Row() {
Column() {
Text(record.fileName)
.fontSize(16)
.fontWeight(FontWeight.Bold)
Text(this.formatTime(record.createTime))
.fontSize(12)
.margin({ top: 4 })
Text(时长: ${this.formatDuration(record.duration)})
.fontSize(12)
.margin({ top: 2 })
.layoutWeight(1)
Row() {
Button('播放')
.onClick(() => {
this.playRecord(record);
})
Button('删除')
.margin({ left: 10 })
.onClick(() => {
this.deleteRecord(record.id);
})
}
.width('100%')
.padding(10)
.backgroundColor('#FFFFFF')
.borderRadius(8)
.margin({ bottom: 10 })
@Builder
buildAudioPlayer() {
Column() {
Text('正在播放: ’ + (this.currentRecord?.fileName || ‘’))
.fontSize(16)
.margin({ bottom: 10 })
// 播放进度条
Slider({ value: 0, min: 0, max: 100 })
.width('80%')
Row() {
Button('暂停')
.onClick(() => {
// 实现暂停逻辑
})
Button('停止')
.margin({ left: 20 })
.onClick(() => {
this.showPlayer = false;
})
.margin({ top: 20 })
.width(‘100%’)
.padding(20)
.backgroundColor('#F5F5F5')
.borderRadius(8)
.margin({ top: 20 })
private async startRecording() {
this.isRecording = await recorderService.startRecording();
private async stopRecording() {
const record = await recorderService.stopRecording();
if (record) {
this.records = [record, ...this.records];
this.isRecording = false;
private async loadRecords() {
this.records = await recorderService.getRecords();
private playRecord(record: AudioRecord) {
this.currentRecord = record;
this.showPlayer = true;
// 实际实现需要调用音频播放服务
private async deleteRecord(recordId: string) {
const success = await recorderService.deleteRecord(recordId);
if (success) {
this.records = this.records.filter(r => r.id !== recordId);
if (this.currentRecord?.id === recordId) {
this.showPlayer = false;
}
private formatTime(timestamp: number): string {
const date = new Date(timestamp);
return {date.getFullYear()}-{date.getMonth() + 1}-{date.getDate()} {date.getHours()}:${date.getMinutes()};
private formatDuration(ms: number): string {
const seconds = Math.floor(ms / 1000);
const mins = Math.floor(seconds / 60);
const secs = seconds % 60;
return {mins}:{secs.toString().padStart(2, '0')};
}
分布式同步增强(ArkTS)
// EnhancedSyncService.ets
import deviceManager from ‘@ohos.distributedDeviceManager’;
class EnhancedSyncService {
private static instance: EnhancedSyncService = null;
private deviceManager: deviceManager.DeviceManager;
private constructor() {
this.initDeviceManager();
public static getInstance(): EnhancedSyncService {
if (!EnhancedSyncService.instance) {
EnhancedSyncService.instance = new EnhancedSyncService();
return EnhancedSyncService.instance;
private async initDeviceManager() {
try {
this.deviceManager = await deviceManager.createDeviceManager('com.example.audiorecorder');
this.deviceManager.on('deviceOnline', (device) => {
this.syncWithDevice(device.deviceId);
});
catch (err) {
console.error('初始化DeviceManager失败:', JSON.stringify(err));
}
public async syncAllDevices() {
const devices = this.deviceManager.getTrustedDeviceListSync();
devices.forEach(device => {
this.syncWithDevice(device.deviceId);
});
private async syncWithDevice(deviceId: string) {
const records = await recorderService.getRecords();
distributedData.sync(RECORD_SYNC_CHANNEL, {
type: 'fullSync',
records: records,
timestamp: Date.now(),
deviceId: this.getLocalDeviceId()
}, {
targetDevice: deviceId
});
private getLocalDeviceId(): string {
// 实际实现需要获取设备ID
return 'local_device';
}
export const enhancedSyncService = EnhancedSyncService.getInstance();
三、项目配置
权限配置
// module.json5
“module”: {
"requestPermissions": [
“name”: “ohos.permission.MICROPHONE”,
"reason": "录制音频"
},
“name”: “ohos.permission.READ_MEDIA”,
"reason": "读取音频文件"
},
“name”: “ohos.permission.WRITE_MEDIA”,
"reason": "保存音频文件"
},
“name”: “ohos.permission.DISTRIBUTED_DATASYNC”,
"reason": "跨设备同步录音记录"
],
"abilities": [
“name”: “MainAbility”,
"type": "page",
"visible": true
],
"distributedNotification": {
"scenarios": [
“name”: “audio_recorder_sync”,
"value": "audio_records"
]
}
资源文件
<!-- resources/base/element/string.json -->
“string”: [
“name”: “app_name”,
"value": "录音机"
},
“name”: “start_record”,
"value": "开始录音"
},
“name”: “stop_record”,
"value": "停止录音"
},
“name”: “play_button”,
"value": "播放"
},
“name”: “delete_button”,
"value": "删除"
},
“name”: “pause_button”,
"value": "暂停"
},
“name”: “stop_button”,
"value": "停止"
},
“name”: “playing_title”,
"value": "正在播放"
},
“name”: “duration_prefix”,
"value": "时长"
]
四、功能扩展
音频播放服务
// AudioPlayerService.ets
import audio from ‘@ohos.multimedia.audio’;
class AudioPlayerService {
private audioPlayer: audio.AudioPlayer;
private currentFilePath: string = ‘’;
async play(filePath: string): Promise<boolean> {
try {
if (this.audioPlayer && this.currentFilePath === filePath) {
await this.audioPlayer.play();
return true;
this.audioPlayer = await audio.createAudioPlayer();
this.currentFilePath = filePath;
const fileDescriptor = await fileIO.open(filePath, 0o2);
await this.audioPlayer.setSource(fileDescriptor);
await this.audioPlayer.play();
return true;
catch (err) {
console.error('播放失败:', JSON.stringify(err));
return false;
}
async pause(): Promise<void> {
if (this.audioPlayer) {
await this.audioPlayer.pause();
}
async stop(): Promise<void> {
if (this.audioPlayer) {
await this.audioPlayer.stop();
await this.audioPlayer.release();
this.audioPlayer = undefined;
this.currentFilePath = ‘’;
}
export const playerService = new AudioPlayerService();
录音文件分享功能
// ShareService.ets
import fileIO from ‘@ohos.fileio’;
import distributedFile from ‘@ohos.distributedfile’;
class ShareService {
async shareRecord(record: AudioRecord, targetDeviceId: string): Promise<boolean> {
try {
// 检查文件是否存在
const isExist = await fileIO.access(record.filePath);
if (!isExist) return false;
// 传输文件到目标设备
await distributedFile.sendFile(targetDeviceId, record.filePath);
// 同步记录信息
distributedData.sync(RECORD_SYNC_CHANNEL, {
type: 'shareRecord',
record: record,
sourceDeviceId: this.getDeviceId(),
targetDeviceId: targetDeviceId
});
return true;
catch (err) {
console.error('分享录音失败:', JSON.stringify(err));
return false;
}
private getDeviceId(): string {
// 实际实现需要获取设备ID
return ‘local_device’;
}
export const shareService = new ShareService();
录音质量设置
// 在AudioRecorderService中添加
class AudioRecorderService {
private quality: ‘low’ ‘medium’
‘high’ = ‘medium’;
public setQuality(quality: ‘low’ ‘medium’
‘high’): void {
this.quality = quality;
private getAudioConfig(): audio.AudioStreamInfo {
switch (this.quality) {
case 'low':
return {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_8000,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
};
case 'high':
return {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,
channels: audio.AudioChannel.CHANNEL_2,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
};
default: // medium
return {
samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_44100,
channels: audio.AudioChannel.CHANNEL_1,
sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,
encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW
};
}
五、总结
通过这个录音机应用的实现,我们学习了:
使用HarmonyOS音频服务进行录音
管理音频文件的存储和读取
使用分布式能力同步录音记录
构建完整的录音、播放、删除功能
实现多设备间的录音文件共享
系统特点:
完整的录音功能实现
跨设备数据同步
友好的用户界面
可扩展的架构设计
这个应用可以进一步扩展为功能更完善的音频工具,如:
添加音频编辑功能
支持多种音频格式
实现云端备份
添加语音转文字功能
支持多语言界面
