MediaPlayer 简介
MediaPlayer 是一个功能强大的媒体播放器,它可以播放来自不同来源的音频和视频文件,

(图片来源网络,侵删)
- 应用资源文件 (res/raw/)
- 应用内部存储 (Internal Storage)
- 外部存储 (SD Card)
- 网络流 (HTTP/HTTPS URL)
它支持多种常见的媒体格式,如 MP3, MP4, 3GP, AAC, WAV, AMR 等。
MediaPlayer 的核心工作流程
使用 MediaPlayer 的标准流程通常遵循以下生命周期,这非常重要,理解了它就能避免大多数错误。
状态机图:
这是一个简化的状态转换图,可以帮助你理解 MediaPlayer 的不同状态和它们之间的转换。
(prepareAsync)
+----------------+ +----------------+ +----------------+
| 初始状态 |----->| 初始化状态 |----->| 准备就绪状态 |
| (Idle) | | (Initialized) | | (Prepared) |
+----------------+ +----------------+ +----------------+
^ | ^ | | |
| | (setDataSource) | | (prepare) | | (start)
| | | | | |
| v | v | v
+----------------+ +----------------+ +----------------+
| 错误状态 |<-----| 准备中状态 |<-----| 播放中状态 |
| (Error) | | (Preparing) | | (Started) |
+----------------+ +----------------+ +----------------+
^ | | |
| (reset) | (seekTo) | | (pause)
| | | |
| v | v
| +----------------+ +----------------+
+---------------| 暂停状态 |------>| 暂停就绪状态 |
| (Paused) | | (PlaybackCompleted) |
+----------------+ +----------------+
关键状态解释:

(图片来源网络,侵删)
- Idle (初始状态): 创建
MediaPlayer对象后的默认状态,可以调用reset(),setDataSource(),setDisplay()。 - Initialized (初始化状态): 成功调用
setDataSource()后进入此状态,可以调用prepare(),prepareAsync(),reset()。 - Prepared (准备就绪状态): 成功调用
prepare()或prepareAsync()后进入此状态,此时可以调用start(),pause(),seekTo(),stop()。 - Started (播放中状态): 调用
start()后进入,可以调用pause(),stop(),seekTo()。 - Paused (暂停状态): 调用
pause()后进入,可以调用start(),stop()。 - PlaybackCompleted (播放完成状态): 播放自然结束时进入,可以调用
start()(重新播放),stop(),seekTo()。 - Error (错误状态): 发生错误时进入,需要调用
reset()或release()来恢复或释放。 - End (结束状态): 调用
release()后进入。MediaPlayer对象不能再被使用。
基本使用步骤(以播放本地资源为例)
下面是一个完整的、可运行的示例,展示了如何播放 res/raw 目录下的音频文件。
添加资源文件
将你的音频文件(my_music.mp3)放入 app/src/main/res/raw/ 目录下,Android 会自动将其作为资源处理。
布局文件 (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<Button
android:id="@+id/btn_play"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
<Button
android:id="@+id/btn_pause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停" />
<Button
android:id="@+id/btn_stop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止" />
</LinearLayout>
Java 代码 (MainActivity.java)
import androidx.appcompat.app.AppCompatActivity;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private MediaPlayer mediaPlayer;
private Button btnPlay, btnPause, btnStop;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btnPlay = findViewById(R.id.btn_play);
btnPause = findViewById(R.id.btn_pause);
btnStop = findViewById(R.id.btn_stop);
btnPlay.setOnClickListener(this);
btnPause.setOnClickListener(this);
btnStop.setOnClickListener(this);
// 1. 创建 MediaPlayer 对象 (Idle 状态)
mediaPlayer = new MediaPlayer();
try {
// 2. 设置数据源 (Initialized 状态)
// 注意:使用 R.raw.xxx 的方式
mediaPlayer.setDataSource(this, R.raw.my_music);
// 3. 准备播放器 (Prepared 状态)
// prepare() 是同步的,会阻塞调用线程,不适合在主线程(UI线程)中使用
// prepareAsync() 是异步的,推荐使用
mediaPlayer.prepare();
// 如果使用异步准备,需要监听 OnPreparedListener
/*
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// 准备完成后,可以在这里开始播放
// mediaPlayer.start();
}
});
mediaPlayer.prepareAsync();
*/
} catch (Exception e) {
e.printStackTrace();
Toast.makeText(this, "初始化 MediaPlayer 失败", Toast.LENGTH_SHORT).show();
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_play:
if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
// 4. 开始播放 (Started 状态)
mediaPlayer.start();
Toast.makeText(this, "开始播放", Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_pause:
if (mediaPlayer != null && mediaPlayer.isPlaying()) {
// 5. 暂停播放 (Paused 状态)
mediaPlayer.pause();
Toast.makeText(this, "暂停播放", Toast.LENGTH_SHORT).show();
}
break;
case R.id.btn_stop:
if (mediaPlayer != null) {
// 6. 停止播放 (Stopped -> Prepared 状态)
// 注意:stop() 后需要重新 prepare() 才能再次 start()
mediaPlayer.stop();
Toast.makeText(this, "停止播放", Toast.LENGTH_SHORT).show();
// 如果想再次播放,需要重新准备
// try {
// mediaPlayer.prepare(); // 重新准备
// mediaPlayer.start();
// } catch (IOException e) {
// e.printStackTrace();
// }
}
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 7. 释放资源 (End 状态)
// 这是至关重要的一步,防止内存泄漏
if (mediaPlayer != null) {
mediaPlayer.release();
mediaPlayer = null;
}
}
}
高级功能与最佳实践
异步准备 (prepareAsync)
在主线程中调用 prepare() 会导致界面卡顿(ANR - Application Not Responding)。强烈推荐使用 prepareAsync()。
mediaPlayer.prepareAsync();
// 设置准备监听器
mediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(MediaPlayer mp) {
// 当准备完成时,这个回调会在主线程被调用
// 此时可以安全地调用 start()
Toast.makeText(context, "准备完成,可以播放", Toast.LENGTH_SHORT).show();
// mediaPlayer.start(); // 如果想自动播放
}
});
错误处理
播放过程中可能会发生各种错误(如网络中断、文件损坏、格式不支持等),必须设置 OnErrorListener 来处理。

(图片来源网络,侵删)
mediaPlayer.setOnErrorListener(new MediaPlayer.OnErrorListener() {
@Override
public boolean onError(MediaPlayer mp, int what, int extra) {
// 发生错误时,返回 true 表示我们已经处理了错误
// 返回 false 则 MediaPlayer 会尝试自己处理(通常会进入错误状态)
Toast.makeText(context, "播放错误: " + what + ", " + extra, Toast.LENGTH_LONG).show();
// 通常在发生错误后,需要重置或释放 MediaPlayer
// reset(); // 或者 release();
return true;
}
});
播放完成监听
当播放到文件末尾时,可以设置监听器来执行后续操作,如播放下一首。
mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText(context, "播放完成", Toast.LENGTH_SHORT).show();
// 在这里执行播放下一首的逻辑
}
});
播放控制
seekTo(int msec): 跳转到指定位置的毫秒数。getCurrentPosition(): 获取当前播放位置的毫秒数。getDuration(): 获取媒体总时长(毫秒)。setVolume(float leftVolume, float rightVolume): 设置左右声道音量(0.0f 到 1.0f)。setLooping(boolean looping): 设置是否循环播放。
播放视频
播放视频和音频的步骤基本相同,但需要额外设置一个 SurfaceHolder 来显示视频画面。
// 在布局中添加一个 SurfaceView
<SurfaceView
android:id="@+id/surfaceView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
// 在代码中
SurfaceView surfaceView = findViewById(R.id.surfaceView);
SurfaceHolder holder = surfaceView.getHolder();
holder.addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
// Surface 创建成功后,设置显示
mediaPlayer.setDisplay(holder);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
// Surface 销毁时,必须清除显示
mediaPlayer.setDisplay(null);
}
});
重要注意事项
- 不要阻塞UI线程: 绝对不要在
onCreate,onStart等生命周期方法中直接调用prepare(),使用prepareAsync()或将准备工作放在后台线程(如AsyncTask,Thread,ExecutorService)中。 - 资源释放 (
release()):MediaPlayer占用大量系统资源(如解码器、文件句柄),当Activity被销毁时(onDestroy),必须调用release()来释放这些资源,否则会导致严重的内存泄漏,甚至可能使其他应用无法使用媒体功能。 - 生命周期管理: 为了避免复杂的生命周期管理问题,推荐使用
MediaPlayer的静态方法create(),它会自动完成初始化、设置数据源和准备操作,并返回一个处于Prepared状态的MediaPlayer对象。// 简化版创建方式 MediaPlayer mediaPlayer = MediaPlayer.create(context, R.raw.my_music); // 此时可以直接调用 start()
但请注意,
create()内部也是调用了prepare(),它是在一个后台线程中执行的,所以不会阻塞UI线程。 - 异常处理: 所有
MediaPlayer的方法都可能抛出IllegalStateException,如果调用时对象的状态不正确,务必参考状态机图,在正确的状态下调用正确的方法。
替代方案:ExoPlayer
虽然 MediaPlayer 非常强大,但它也存在一些缺点:
- 功能有限: 不支持 DASH, HLS 等现代流媒体协议。
- 性能问题: 在某些设备上可能性能不佳,尤其是在处理高清视频时。
- 扩展性差: 自定义和扩展功能比较困难。
Google 推出了 ExoPlayer 作为 MediaPlayer 的现代替代品。
ExoPlayer 的优点:
- 高度可定制: 模块化设计,可以轻松替换组件(如渲染器、来源、解码器)。
- 功能强大: 原生支持 DASH, HLS, SmoothStreaming, MP4, M4A, MP3, AAC, OGG 等格式。
- 性能优异: 经过优化,性能通常优于
MediaPlayer。 - 活跃维护: 由 Google 团队持续维护和更新。
对于新的项目,尤其是需要播放网络视频、支持直播或对播放体验有较高要求的应用,强烈推荐直接使用 ExoPlayer。
| 特性 | MediaPlayer | ExoPlayer |
|---|---|---|
| 易用性 | 简单,几行代码即可使用 | 较复杂,需要更多配置 |
| 功能 | 基础播放功能强大 | 极其强大,支持现代流媒体协议 |
| 性能 | 一般,依赖设备硬件 | 优秀,高度优化 |
| 定制性 | 差 | 极高,完全模块化 |
| 适用场景 | 简单的音频/视频播放,播放本地文件或简单网络流 | 复杂的视频应用,直播,DASH/HLS播放,需要高度定制 |
建议:
- 如果只是播放一个简单的背景音乐或本地视频,
MediaPlayer足够用且更简单。 - 如果你的应用核心功能是视频播放,或者需要播放网络视频、直播,请直接选择 ExoPlayer。
