1. 项目背景与目标
作为一名长期从事跨平台开发的工程师,我最近在探索如何将Flutter应用深度适配HarmonyOS 6.0系统。EchoMusic(回声音乐)这个项目源于一个实际需求——开发一款能够实现高质量录音、即时回放和音频处理的移动应用。录音控制区域作为整个应用的核心功能模块,其实现质量直接决定了用户体验的好坏。
在HarmonyOS 6.0环境下,音频采集和处理有其独特的系统特性。比如,HarmonyOS的音频子系统采用了全新的分布式架构,支持跨设备音频流转,这对我们设计录音功能提出了新的要求。同时,Flutter作为跨平台框架,在访问原生音频API时需要特别注意平台通道的调用效率问题。
这个模块需要实现的核心功能包括:
- 高质量的音频采集(支持48kHz采样率)
- 实时波形可视化
- 录音状态控制(开始/暂停/继续/停止)
- 录音时长和文件大小实时显示
- 异常情况处理(如权限拒绝、存储空间不足等)
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备与项目配置
2.1 Flutter与HarmonyOS环境搭建
首先需要确保开发环境正确配置。我使用的是Flutter 3.13.0稳定版,这个版本对HarmonyOS的支持相对完善。安装过程中有几个关键点需要注意:
bash复制# 安装Flutter SDK后需要特别配置的环节
flutter pub global activate fvm
fvm install 3.13.0
fvm use 3.13.0
对于HarmonyOS开发,需要在项目的android目录下进行额外配置。这是因为目前Flutter官方尚未直接支持HarmonyOS,我们需要通过Android兼容层来实现:
gradle复制// android/build.gradle 需要添加的配置
harmony {
compileSdkVersion 6
minSdkVersion 5
targetSdkVersion 6
}
2.2 音频相关依赖包选择
经过对比测试,我最终选择了以下依赖组合:
yaml复制dependencies:
flutter_sound: ^9.2.13 # 核心录音功能
just_audio: ^0.9.35 # 回放功能
path_provider: ^2.0.15 # 文件路径处理
permission_handler: ^10.4.0 # 权限管理
synchronized: ^3.1.0 # 线程同步
选择flutter_sound是因为它提供了最完整的录音控制API,包括:
- 采样率设置
- 编码格式选择(我们使用AAC编码)
- 实时音频数据回调
- 录音状态精细控制
注意:在HarmonyOS上使用这些包时,需要特别检查每个插件的原生代码实现是否兼容鸿蒙的API。我在实践中发现有些插件需要手动修改其Android实现才能正常工作。
3. 录音控制区域UI实现
3.1 核心组件结构设计
录音控制区域采用组合式Widget设计,整体结构如下:
dart复制class RecordingControl extends StatefulWidget {
@override
_RecordingControlState createState() => _RecordingControlState();
}
class _RecordingControlState extends State<RecordingControl> {
// 状态变量和控制逻辑
}
主要包含以下几个子组件:
- 波形可视化区域(使用CustomPaint实现)
- 控制按钮组(开始/暂停/停止)
- 信息显示区域(时长、文件大小)
- 状态指示器(录音中、暂停中等)
3.2 波形可视化实现
实时波形显示是提升用户体验的关键。我们通过flutter_sound的onProgress回调获取实时音频数据:
dart复制FlutterSoundPlayer _audioPlayer = FlutterSoundPlayer();
FlutterSoundRecorder _audioRecorder = FlutterSoundRecorder();
void _startRecording() async {
await _audioRecorder.openAudioSession();
await _audioRecorder.startRecorder(
toFile: _recordingPath,
codec: Codec.aacADTS,
sampleRate: 48000,
onProgress: (duration) {
// 获取实时音频数据
_updateWaveform(duration);
},
);
}
波形绘制使用CustomPaint,核心绘制逻辑:
dart复制class WaveformPainter extends CustomPainter {
final List<double> amplitudes;
@override
void paint(Canvas canvas, Size size) {
final paint = Paint()
..color = Colors.blue
..strokeWidth = 2.0
..style = PaintingStyle.stroke;
final middle = size.height / 2;
final widthPerSample = size.width / amplitudes.length;
for (int i = 0; i < amplitudes.length; i++) {
final x = i * widthPerSample;
final amplitude = amplitudes[i] * middle;
canvas.drawLine(
Offset(x, middle - amplitude),
Offset(x, middle + amplitude),
paint,
);
}
}
}
3.3 控制按钮状态管理
录音控制需要处理多种状态:
- 初始状态(等待开始)
- 录音中
- 暂停中
- 停止完成
我们使用一个状态枚举来管理:
dart复制enum RecordingState {
idle,
recording,
paused,
stopped,
}
按钮组根据当前状态动态变化:
dart复制Widget _buildControlButtons() {
switch (_recordingState) {
case RecordingState.idle:
return _buildStartButton();
case RecordingState.recording:
return Row(
children: [
_buildPauseButton(),
SizedBox(width: 20),
_buildStopButton(),
],
);
case RecordingState.paused:
return Row(
children: [
_buildResumeButton(),
SizedBox(width: 20),
_buildStopButton(),
],
);
case RecordingState.stopped:
return _buildSaveButton();
}
}
4. HarmonyOS特有功能适配
4.1 分布式音频能力集成
HarmonyOS 6.0的分布式能力允许音频在不同设备间流转。我们需要通过平台通道调用鸿蒙原生API:
dart复制static const platform = MethodChannel('com.echomusic/audio');
Future<void> _enableDistributedRecording() async {
try {
await platform.invokeMethod('enableDistributedAudio', {
'deviceId': _selectedDeviceId,
'audioProfile': 'music_high_quality',
});
} on PlatformException catch (e) {
print("分布式录音启用失败: ${e.message}");
}
}
对应的HarmonyOS原生代码(Java):
java复制public class AudioPlugin implements FlutterPlugin {
@Override
public void onAttachedToEngine(FlutterPluginBinding binding) {
final MethodChannel channel = new MethodChannel(
binding.getBinaryMessenger(),
"com.echomusic/audio"
);
channel.setMethodCallHandler((call, result) -> {
if (call.method.equals("enableDistributedAudio")) {
String deviceId = call.argument("deviceId");
// 调用鸿蒙分布式音频API
DistributedAudioManager.getInstance().startRecording(deviceId);
result.success(null);
}
});
}
}
4.2 鸿蒙音频权限处理
HarmonyOS的权限系统与Android有所不同,需要特别处理:
dart复制Future<bool> _checkHarmonyPermissions() async {
if (Platform.isHarmonyOS) {
try {
final result = await platform.invokeMethod('checkAudioPermissions');
return result as bool;
} catch (e) {
return false;
}
} else {
return await Permission.microphone.isGranted;
}
}
5. 性能优化与调试技巧
5.1 音频线程优化
录音过程中需要特别注意线程管理,避免UI卡顿:
dart复制final _lock = Lock(); // 使用synchronized包提供的锁
void _handleAudioData(List<double> samples) async {
await _lock.synchronized(() {
// 处理音频数据
_waveformData = _processSamples(samples);
if (mounted) {
setState(() {});
}
});
}
5.2 常见问题排查
在实际开发中,我遇到了几个典型问题:
- 录音延迟问题:
- 现象:开始录音后约1秒才有声音
- 原因:HarmonyOS音频缓冲区默认设置过大
- 解决:通过平台通道设置缓冲区大小
dart复制await platform.invokeMethod('setAudioBufferSize', {
'sizeInMs': 100, // 100毫秒
});
- 波形显示卡顿:
- 现象:录音时波形更新不流畅
- 原因:频繁setState导致界面重绘
- 解决:使用ValueNotifier优化
dart复制final _waveNotifier = ValueNotifier<List<double>>([]);
void initState() {
super.initState();
_waveNotifier.addListener(() {
if (mounted) setState(() {});
});
}
void _updateWaveform(List<double> samples) {
// 只在数据变化足够大时更新
if (_shouldUpdate(samples)) {
_waveNotifier.value = samples;
}
}
- 鸿蒙设备兼容性问题:
- 现象:在某些鸿蒙设备上录音失败
- 原因:设备特定的音频编解码支持差异
- 解决:动态检测可用编解码器
dart复制Future<Codec> _getSupportedCodec() async {
if (Platform.isHarmonyOS) {
try {
final codecName = await platform.invokeMethod('getPreferredAudioCodec');
return Codec.values.firstWhere(
(c) => c.toString() == codecName,
orElse: () => Codec.aacADTS,
);
} catch (e) {
return Codec.aacADTS;
}
}
return Codec.aacADTS;
}
6. 完整实现示例
以下是录音控制区域的核心实现代码:
dart复制class RecordingControl extends StatefulWidget {
final Function(String) onRecordingComplete;
const RecordingControl({required this.onRecordingComplete});
@override
_RecordingControlState createState() => _RecordingControlState();
}
class _RecordingControlState extends State<RecordingControl> {
final _audioRecorder = FlutterSoundRecorder();
final _waveNotifier = ValueNotifier<List<double>>([]);
RecordingState _recordingState = RecordingState.idle;
String _recordingPath = '';
Duration _duration = Duration.zero;
@override
void initState() {
super.initState();
_initRecorder();
}
Future<void> _initRecorder() async {
await _audioRecorder.openAudioSession();
_recordingPath = await _getRecordingPath();
}
Future<String> _getRecordingPath() async {
final dir = await getApplicationDocumentsDirectory();
return '${dir.path}/recording_${DateTime.now().millisecondsSinceEpoch}.aac';
}
Future<void> _startRecording() async {
if (!await _checkPermissions()) return;
setState(() => _recordingState = RecordingState.recording);
await _audioRecorder.startRecorder(
toFile: _recordingPath,
codec: await _getSupportedCodec(),
sampleRate: 48000,
onProgress: (duration) {
_duration = duration;
_updateWaveform(_audioRecorder.getProgress);
},
);
}
// 其他控制方法(暂停、继续、停止)...
@override
Widget build(BuildContext context) {
return Column(
children: [
ValueListenableBuilder(
valueListenable: _waveNotifier,
builder: (_, samples, __) {
return CustomPaint(
size: Size(MediaQuery.of(context).size.width, 120),
painter: WaveformPainter(samples),
);
},
),
SizedBox(height: 20),
_buildTimerDisplay(),
SizedBox(height: 20),
_buildControlButtons(),
],
);
}
@override
void dispose() {
_audioRecorder.closeAudioSession();
super.dispose();
}
}
7. 测试与验证方案
为确保录音控制区域的质量,我设计了以下测试用例:
-
基础功能测试:
- 正常录音流程(开始-停止)
- 暂停/继续功能
- 录音时长准确性
- 生成文件可播放性
-
异常场景测试:
- 权限被拒绝时的处理
- 存储空间不足时的处理
- 来电中断处理
- 设备旋转时的状态保持
-
性能测试:
- 长时间录音的内存占用
- 高采样率下的CPU使用率
- 分布式录音的延迟测试
测试代码示例:
dart复制void main() {
testWidgets('录音基础流程测试', (WidgetTester tester) async {
await tester.pumpWidget(MaterialApp(
home: Scaffold(
body: RecordingControl(onRecordingComplete: (_) {}),
),
));
// 模拟点击开始按钮
await tester.tap(find.byIcon(Icons.mic));
await tester.pump();
// 验证状态变为录音中
expect(find.byIcon(Icons.pause), findsOneWidget);
// 模拟录音5秒
await tester.pump(Duration(seconds: 5));
// 模拟点击停止
await tester.tap(find.byIcon(Icons.stop));
await tester.pump();
// 验证完成回调被调用
expect(/* 验证回调 */, isTrue);
});
}
8. 项目总结与扩展思考
在实际开发这个录音控制模块的过程中,有几个关键经验值得分享:
-
跨平台差异处理:
- 鸿蒙和Android在音频处理上存在细微差别,特别是缓冲区管理和权限处理
- 建议为每个平台编写特定的原生代码,通过统一接口暴露给Flutter层
-
状态管理选择:
- 对于复杂的录音状态,使用状态机模式比简单的布尔标志更可靠
- 考虑使用Riverpod等状态管理库来简化跨组件状态共享
-
性能平衡技巧:
- 波形渲染不需要实时更新,每秒15-20帧已经足够流畅
- 对于长时间录音,建议定期将内存中的波形数据持久化,避免内存增长
这个模块后续可以考虑的扩展方向:
- 添加实时音频效果处理(如降噪、均衡器)
- 支持多轨录音
- 集成鸿蒙的AI音频分析能力(如语音转文字)
- 实现跨设备协同录音(利用鸿蒙分布式能力)
录音功能作为音频类应用的核心,其稳定性和性能至关重要。通过Flutter与HarmonyOS的结合,我们既能享受跨平台开发的效率,又能利用原生系统的先进特性。在开发过程中,特别需要注意平台差异和性能优化,这些经验也同样适用于其他多媒体功能的开发。
