4. MediaPlayer播放本地音频文件
任务说明
本案例使用 Android 的 MediaPlayer 播放应用内部存储的本地音频文件(MP3/AAC),实现播放、暂停、停止三个基本控制,并展示播放状态与时长信息。音频文件放在 app/src/main/assets/ 目录下,通过 AssetFileDescriptor 打开,避免路径权限问题。
环境与依赖
- Android Studio,
minSdkVersion 21及以上。 - 无需第三方库,仅使用
android.media.MediaPlayer。 - 准备一个音频文件,命名为
sample.mp3(或sample.aac),放入app/src/main/assets/。
核心实现
用一个 MediaPlayer 实例管理生命周期:prepare() 后进入 Prepared 状态,start() 播放,pause() 暂停,stop() 停止并回到 Idle 状态。停止后需重新 prepare() 才能再次播放,因此把“准备”封装成独立方法。播放结束通过 OnCompletionListener 复位按钮状态。
完整代码
布局文件 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:orientation="vertical"
android:gravity="center"
android:padding="24dp">
<TextView
android:id="@+id/tvStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="18sp"
android:text="未播放" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="24dp">
<Button
android:id="@+id/btnPlay"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="播放" />
<Button
android:id="@+id/btnPause"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="暂停" />
<Button
android:id="@+id/btnStop"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="停止" />
</LinearLayout>
</LinearLayout>
主逻辑 MainActivity.java:
package com.example.audioplayer;
import android.content.res.AssetFileDescriptor;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import java.io.IOException;
public class MainActivity extends AppCompatActivity {
private MediaPlayer player;
private TextView tvStatus;
private boolean prepared = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvStatus = findViewById(R.id.tvStatus);
Button btnPlay = findViewById(R.id.btnPlay);
Button btnPause = findViewById(R.id.btnPause);
Button btnStop = findViewById(R.id.btnStop);
player = new MediaPlayer();
player.setOnCompletionListener(mp -> {
prepared = false;
tvStatus.setText("播放结束");
});
btnPlay.setOnClickListener(v -> {
if (!prepared) {
if (!preparePlayer()) return;
}
player.start();
tvStatus.setText("播放中,时长 " + player.getDuration() + " ms");
});
btnPause.setOnClickListener(v -> {
if (prepared && player.isPlaying()) {
player.pause();
tvStatus.setText("已暂停");
}
});
btnStop.setOnClickListener(v -> {
if (prepared) {
player.stop();
prepared = false;
tvStatus.setText("已停止");
}
});
}
private boolean preparePlayer() {
try {
AssetFileDescriptor afd = getAssets().openFd("sample.mp3");
player.reset();
player.setDataSource(afd.getFileDescriptor(),
afd.getStartOffset(), afd.getLength());
afd.close();
player.prepare();
prepared = true;
return true;
} catch (IOException e) {
tvStatus.setText("加载失败:" + e.getMessage());
return false;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if (player != null) {
player.release();
player = null;
}
}
}
运行与验证
- 把音频文件放入
app/src/main/assets/sample.mp3,若用 AAC 则改名为sample.aac并同步修改代码中的文件名。 - 连接设备或启动模拟器,点击 Run 安装运行。
- 点击“播放”:应听到音频,状态栏显示“播放中,时长 xxx ms”。
- 点击“暂停”:声音停止,状态显示“已暂停”;再次点击“播放”应从暂停处继续。
- 点击“停止”:声音停止,状态显示“已停止”;再次点击“播放”应从头重新播放。
- 播放到结尾时状态自动变为“播放结束”。
通过条件:三个按钮行为与上述一致,无崩溃,退出页面后音频停止(onDestroy 中已 release)。
常见问题
- 若报
IOException,检查 assets 文件名与代码是否一致,且文件确实被打包进 APK。 - 停止后直接调用
start()会抛IllegalStateException,因此代码用prepared标志强制重新prepare()。 - 播放长音频建议放到后台线程
prepareAsync(),本案例音频较短,用同步prepare()即可。