2. 使用MediaPlayer播放网络音频

任务说明

本案例将实现一个最小可用的网络音频播放器:界面包含播放/暂停按钮、进度条和状态提示文本,核心逻辑使用 MediaPlayer 播放远程 MP3 文件,并处理缓冲等待与错误状态。你需要准备一个可访问的 MP3 直链用于测试。

核心实现

首先在 AndroidManifest.xml 中声明网络权限:

<uses-permission android:name="android.permission.INTERNET" />

布局文件 activity_main.xml 包含三个核心控件:

<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">

    <Button
        android:id="@+id/btnPlayPause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="播放" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="20dp"
        android:max="100" />

    <TextView
        android:id="@+id/tvStatus"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="12dp"
        android:text="就绪" />
</LinearLayout>

主活动 MainActivity.java 中完成 MediaPlayer 的创建、状态监听与界面更新:

package com.example.netaudio;

import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.Handler;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {
    private MediaPlayer mediaPlayer;
    private Button btnPlayPause;
    private ProgressBar progressBar;
    private TextView tvStatus;
    private Handler handler = new Handler();
    private boolean isPlaying = false;
    private static final String AUDIO_URL =
            "https://www.soundhelix.com/examples/mp3/SoundHelix-Song-1.mp3";

    private Runnable progressRunnable = new Runnable() {
        @Override
        public void run() {
            if (mediaPlayer != null && isPlaying) {
                int current = mediaPlayer.getCurrentPosition();
                int duration = mediaPlayer.getDuration();
                if (duration > 0) {
                    progressBar.setProgress(current * 100 / duration);
                }
                handler.postDelayed(this, 500);
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        btnPlayPause = findViewById(R.id.btnPlayPause);
        progressBar = findViewById(R.id.progressBar);
        tvStatus = findViewById(R.id.tvStatus);

        mediaPlayer = new MediaPlayer();
        mediaPlayer.setOnPreparedListener(mp -> {
            tvStatus.setText("缓冲完成,开始播放");
            mp.start();
            isPlaying = true;
            btnPlayPause.setText("暂停");
            handler.post(progressRunnable);
        });

        mediaPlayer.setOnBufferingUpdateListener((mp, percent) -> {
            // percent 表示缓冲百分比,可用于显示缓冲进度
            if (!isPlaying) {
                tvStatus.setText("缓冲中:" + percent + "%");
            }
        });

        mediaPlayer.setOnErrorListener((mp, what, extra) -> {
            tvStatus.setText("播放错误,请检查网络或URL");
            isPlaying = false;
            btnPlayPause.setText("播放");
            return true;
        });

        mediaPlayer.setOnCompletionListener(mp -> {
            tvStatus.setText("播放完成");
            isPlaying = false;
            btnPlayPause.setText("重新播放");
            progressBar.setProgress(100);
        });

        btnPlayPause.setOnClickListener(v -> {
            if (!isPlaying) {
                try {
                    mediaPlayer.reset();
                    mediaPlayer.setDataSource(AUDIO_URL);
                    mediaPlayer.prepareAsync();
                    tvStatus.setText("正在缓冲...");
                    btnPlayPause.setEnabled(false);
                } catch (Exception e) {
                    tvStatus.setText("初始化失败:" + e.getMessage());
                }
            } else {
                mediaPlayer.pause();
                isPlaying = false;
                btnPlayPause.setText("继续");
                handler.removeCallbacks(progressRunnable);
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(progressRunnable);
        if (mediaPlayer != null) {
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }
}

关键逻辑说明

  • 缓冲处理:调用 prepareAsync() 异步准备,通过 setOnPreparedListener 获知缓冲完成;setOnBufferingUpdateListener 实时获取缓冲百分比并更新状态文本。
  • 错误处理:setOnErrorListener 捕获网络异常、URL 无效等错误,重置界面状态并提示用户。
  • 进度更新:使用 Handler 每 500ms 查询播放位置并更新进度条,暂停或销毁时移除回调。

验证方法

将项目部署到 Android 设备或模拟器(需联网),点击“播放”按钮。预期观察:

  1. 状态文本先显示“正在缓冲...”,随后出现“缓冲中:xx%”,最终变为“缓冲完成,开始播放”;
  2. 进度条随播放推进,按钮文字变为“暂停”;
  3. 点击“暂停”后进度条停止,按钮变为“继续”;点击“继续”恢复播放;
  4. 断开网络后重新播放,状态文本应显示“播放错误,请检查网络或URL”,界面不崩溃。

若以上行为均符合,则案例验证通过。注意:首次播放需等待缓冲完成,实际耗时取决于网络状况。

MediaPlayer 网络音频播放状态机 IDLE 初始状态 INITIALIZED setDataSource() PREPARING prepareAsync() PREPARED 缓冲完成 STARTED start() 播放中 PAUSED pause() 暂停 COMPLETED 播放完成 ERROR 错误状态 reset+setDataSource prepareAsync onPrepared start() pause() start() onCompletion onError reset() 关键回调与事件 onPrepared → 缓冲完成,可调用 start() onBufferingUpdate → 缓冲百分比回调(网络不佳时触发) onError → 网络错误/URL无效时触发 onCompletion → 自然播放结束