第21章 多媒体开发:让APP“有声有色”
多媒体开发,说白了就是让你的APP能播放音乐、视频,能拍照录像。这几乎是每个商业APP的标配功能。我这些年做过的项目,从短视频工具到在线教育,几乎都离不开这些技术。今天我就把多媒体开发的几个核心模块——音频播放、视频播放、相机开发、录制、以及高级播放器——给你讲透。
本章核心知识点
- MediaPlayer:音频播放的“老大哥”
- VideoView:快速实现视频播放
- CameraX:现代相机开发首选
- MediaRecorder:音视频录制
- ExoPlayer:高级播放器的王者
21.1 MediaPlayer:音频播放的“老大哥”
MediaPlayer是Android官方提供的音频/视频播放类。它支持本地文件、网络流媒体、资源文件等多种来源。我个人习惯把它叫做“万能播放器”,因为它能处理大多数常见格式。
先看一个最简单的音频播放示例:
MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.music);
mediaPlayer.start(); // 开始播放
嗯,就这么简单。但实际项目中,你需要处理生命周期。我曾经在一个音乐APP里踩过坑——播放到一半Activity被回收了,MediaPlayer没释放,结果内存泄漏。所以一定要在onDestroy里释放:
@Override
protected void onDestroy() {
super.onDestroy();
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
小技巧:MediaPlayer的状态机比较复杂。你想想看,它从Idle到Initialized到Prepared再到Started,每个状态都有对应的合法操作。我建议你画个状态图贴在工位上,避免在错误状态调用错误方法。
MediaPlayer支持设置监听器,比如播放完成、出错等:
mediaPlayer.setOnCompletionListener(mp -> {
// 播放完成,可以播下一首
Log.d("MediaPlayer", "播放完成");
});
mediaPlayer.setOnErrorListener((mp, what, extra) -> {
Log.e("MediaPlayer", "播放出错: what=" + what + ", extra=" + extra);
return true; // 返回true表示已处理错误
});
21.2 VideoView:快速实现视频播放
VideoView是MediaPlayer的“好搭档”。它把MediaPlayer和SurfaceView封装在一起,让你几行代码就能播放视频。说白了,它就是懒人神器。
VideoView videoView = findViewById(R.id.videoView);
videoView.setVideoPath("https://example.com/video.mp4");
videoView.start();
但要注意,VideoView有一些限制。我记得有一次做直播回放功能,VideoView对HLS流支持不太好,画面总是卡顿。后来我换成了ExoPlayer才解决。
避坑指南:VideoView默认不处理屏幕旋转。如果你需要全屏播放,得自己监听屏幕方向变化,动态调整VideoView的尺寸。我曾经因为没处理这个,被测试小姐姐提了3个bug……
VideoView的常用方法:
| 方法 | 说明 |
|---|---|
| setVideoPath(String) | 设置视频文件路径 |
| setVideoURI(Uri) | 设置视频URI |
| start() | 开始播放 |
| pause() | 暂停播放 |
| seekTo(int) | 跳转到指定位置(毫秒) |
| getCurrentPosition() | 获取当前播放位置 |
| setOnPreparedListener() | 准备完成监听 |
21.3 CameraX:现代相机开发首选
CameraX是Google推出的Jetpack相机库。它解决了Camera API的碎片化问题。说白了,你写一套代码,就能兼容从Android 5.0到最新版本的所有设备。
我强烈推荐用CameraX替代旧的Camera API。为什么?因为CameraX自动处理了生命周期、旋转、预览尺寸等麻烦事。你想想看,以前用Camera API时,要自己写SurfaceView、处理相机旋转、管理生命周期……那叫一个痛苦。
CameraX的核心组件:
- Preview:相机预览
- ImageCapture:拍照
- ImageAnalysis:图像分析(比如扫码)
- VideoCapture:视频录制(CameraX 1.1+)
看一个简单的拍照示例:
// 获取ProcessCameraProvider
ProcessCameraProvider cameraProvider = ProcessCameraProvider.getInstance(context).get();
// 创建预览用例
Preview preview = new Preview.Builder().build();
preview.setSurfaceProvider(previewView.getSurfaceProvider());
// 创建拍照用例
ImageCapture imageCapture = new ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build();
// 绑定到生命周期
Camera camera = cameraProvider.bindToLifecycle(
lifecycleOwner,
CameraSelector.DEFAULT_BACK_CAMERA,
preview,
imageCapture
);
// 拍照
imageCapture.takePicture(
outputFileOptions,
ContextCompat.getMainExecutor(context),
new ImageCapture.OnImageSavedCallback() {
@Override
public void onImageSaved(@NonNull ImageCapture.OutputFileResults output) {
Log.d("CameraX", "照片保存成功");
}
@Override
public void onError(@NonNull ImageCaptureException exception) {
Log.e("CameraX", "拍照失败", exception);
}
}
);
个人经验:CameraX的ImageAnalysis用例非常强大。我做过一个扫码APP,用ImageAnalysis配合ML Kit,识别速度比传统ZXing快了一倍。而且CameraX自动处理了图像格式转换,省了不少事。
21.4 MediaRecorder:音视频录制
MediaRecorder是Android官方提供的录制类。它可以录制音频和视频,输出为MP4、3GP等格式。我建议你用它来做简单的录制功能,比如录音笔、短视频拍摄。
先看一个音频录制的例子:
MediaRecorder recorder = new MediaRecorder();
recorder.setAudioSource(MediaRecorder.AudioSource.MIC);
recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
recorder.setOutputFile(getExternalCacheDir() + "/record.mp4");
try {
recorder.prepare();
recorder.start();
Log.d("MediaRecorder", "开始录制");
} catch (IOException e) {
e.printStackTrace();
}
// 停止录制
recorder.stop();
recorder.release();
避坑指南:MediaRecorder的API调用顺序非常严格。你必须先setAudioSource,再setOutputFormat,再setAudioEncoder……顺序错了就会抛IllegalStateException。我曾经在重构代码时不小心调换了顺序,排查了半天才发现。
视频录制稍微复杂一点,需要配合Camera或CameraX:
// 使用CameraX的VideoCapture
VideoCapture videoCapture = new VideoCapture.Builder()
.setBitRate(10_000_000) // 10Mbps
.setVideoFrameRate(30) // 30fps
.build();
// 开始录制
videoCapture.startRecording(
videoFile,
ContextCompat.getMainExecutor(context),
new VideoCapture.OnVideoSavedCallback() {
@Override
public void onVideoSaved(@NonNull VideoCapture.OutputFileResults output) {
Log.d("CameraX", "视频录制完成");
}
@Override
public void onError(int videoCaptureError, @NonNull String message, @Nullable Throwable cause) {
Log.e("CameraX", "录制失败: " + message);
}
}
);
21.5 ExoPlayer:高级播放器的王者
ExoPlayer是Google官方推荐的播放器库。它比MediaPlayer强大得多,支持DASH、HLS、SmoothStreaming等流媒体协议,还支持自定义渲染器、DRM加密等高级功能。
我为什么说ExoPlayer是王者?因为它解决了MediaPlayer的很多痛点:
- MediaPlayer不支持动态切换音轨/字幕
- MediaPlayer对HLS支持不好
- MediaPlayer在低端设备上解码性能差
- MediaPlayer不能自定义渲染流程
ExoPlayer的基本用法:
// 创建播放器
ExoPlayer player = new ExoPlayer.Builder(context).build();
// 创建媒体项
MediaItem mediaItem = MediaItem.fromUri("https://example.com/video.mp4");
// 设置媒体项并准备播放
player.setMediaItem(mediaItem);
player.prepare();
player.play();
ExoPlayer支持丰富的配置:
// 自定义缓存
Cache cache = new SimpleCache(
cacheDir,
new LeastRecentlyUsedCacheEvictor(50 * 1024 * 1024) // 50MB缓存
);
CacheDataSource.Factory cacheFactory = new CacheDataSource.Factory().setCache(cache);
// 创建播放器时传入自定义数据源工厂
ExoPlayer player = new ExoPlayer.Builder(context)
.setMediaSourceFactory(new DefaultMediaSourceFactory(cacheFactory))
.build();
核心优势:ExoPlayer支持模块化架构。你可以替换掉默认的音频渲染器、视频渲染器、甚至解码器。比如你想用FFmpeg解码,只需要实现一个自定义Renderer即可。这种灵活性是MediaPlayer完全不具备的。
ExoPlayer的监听器体系也很完善:
player.addListener(new Player.Listener() {
@Override
public void onPlaybackStateChanged(int playbackState) {
switch (playbackState) {
case Player.STATE_READY:
Log.d("ExoPlayer", "准备就绪");
break;
case Player.STATE_BUFFERING:
Log.d("ExoPlayer", "缓冲中...");
break;
case Player.STATE_ENDED:
Log.d("ExoPlayer", "播放结束");
break;
}
}
@Override
public void onPlayerError(PlaybackException error) {
Log.e("ExoPlayer", "播放错误: " + error.getMessage());
}
});
个人建议:如果你的APP需要播放网络视频,尤其是流媒体,直接上ExoPlayer。别在MediaPlayer上浪费时间。我做过一个在线教育APP,用ExoPlayer实现了倍速播放、后台播放、画中画等功能,开发效率非常高。
最后,总结一下这五个组件的选择建议:
| 场景 | 推荐组件 | 理由 |
|---|---|---|
| 简单音频播放 | MediaPlayer | 轻量、易用 |
| 简单视频播放 | VideoView | 快速集成 |
| 相机拍照/预览 | CameraX | 兼容性好、生命周期安全 |
| 音视频录制 | MediaRecorder + CameraX | 官方支持、稳定 |
| 流媒体/高级播放 | ExoPlayer | 功能强大、可定制 |
多媒体开发这块,说白了就是“选对工具,事半功倍”。你想想看,如果非要用MediaPlayer去播HLS流,那不是给自己找麻烦吗?我建议你根据实际场景,选择合适的组件。遇到复杂需求,优先考虑ExoPlayer和CameraX,它们代表了Android多媒体开发的未来方向。
公众号:蓝海资料掘金营,微信deep3321