Android-MediaSession-播放流程和例子

一、流程

整体架构概览

客户端 → 服务端:控制指令流向

  1. 发起控制请求

// 客户端:通过 TransportControls 发送指令

mediaController.getTransportControls().play();

mediaController.getTransportControls().pause();

mediaController.getTransportControls().seekTo(1000);

  1. IPC 传输层

    // MediaControllerCompat 内部实现(简化)
    public void play() {
    // 通过 IMediaSession 接口跨进程调用
    mSessionBinder.play(mPackageName, mCb, getSeqNumber());
    }

实际是通过 Binder 调用服务端的 IMediaSession.Stub 实现。

  1. 服务端接收处理

    // MediaSessionService 端
    private final IMediaSession.Stub mSessionBinder = new IMediaSession.Stub() {
    @Override
    public void play(String packageName, IMediaControllerCallback cb, int seq) {
    // 切换到主线程处理
    mHandler.post(() -> {
    // 调用 MediaSession.Callback
    mSessionCallback.onPlay();
    });
    }
    };

  2. 最终执行播放逻辑

    // 你的 MediaSession.Callback 实现
    @Override
    public void onPlay() {
    Log.d(TAG, "收到播放指令");
    if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
    mediaPlayer.start();
    // 关键:更新 PlaybackState 通知所有监听者
    updatePlaybackState(PlaybackStateCompat.STATE_PLAYING);
    }
    }

服务端 → 客户端:状态广播流向

  1. 服务端状态变更

    private void updatePlaybackState(int state) {
    // 构建新状态
    PlaybackStateCompat playbackState = new PlaybackStateCompat.Builder()
    .setState(state, position, playbackRate)
    .setActions(availableActions)
    .build();

    复制代码
     // 关键:设置到 MediaSession
     mediaSession.setPlaybackState(playbackState);

    }

  2. 系统广播机制

MediaSession 内部维护所有连接的 MediaController 列表:

复制代码
// MediaSession 内部
void setPlaybackState(PlaybackStateCompat state) {
    mPlaybackState = state;
    
    // 遍历所有控制器发送更新
    for (MediaController controller : connectedControllers) {
        controller.notifyPlaybackStateChanged(state);
    }
}
  1. 客户端接收回调

    // 客户端的 Callback
    @Override
    public void onPlaybackStateChanged(PlaybackStateCompat state) {
    runOnUiThread(() -> {
    // 更新 UI
    updatePlayButton(state.getState() == STATE_PLAYING);
    updateProgress(state.getPosition());
    });
    }

关键源码路径(Android 11)

frameworks/base/media/java/android/media/session/

├── MediaSession.java # 服务端实现

├── MediaController.java # 客户端实现

└── IMediaSession.aidl # Binder 接口定义

frameworks/support/v4/media/session/

├── MediaSessionCompat.java # 兼容库封装

└── MediaControllerCompat.java

调试与问题排查

  1. 常见问题原因

现象 排查点

控制无响应 MediaSession 未 setActive(true)

状态不同步 忘记调用 setPlaybackState()

回调丢失 未 registerCallback() 或线程阻塞

  1. 调试代码

// 打印调用堆栈

Log.d("MediaFlow", "调用来源: " + Thread.currentThread().getStackTrace()[2]);

// 验证 Session 活性

if (!mediaSession.isActive()) {

Log.w(TAG, "Session 未激活,控制指令将被忽略");

}

// 检查控制器连接

if (mediaController.getSessionToken() == null) {

Log.e(TAG, "Controller 未连接到 Session");

}

二、例子

包含音频焦点和方控处理

服务端 (MusicPlayerService.java)

复制代码
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.media.AudioAttributes;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;

import java.io.IOException;

public class MusicPlayerService extends Service 
        implements AudioManager.OnAudioFocusChangeListener {
    
    private static final String TAG = "MusicPlayerService";
    
    // 音频焦点相关
    private AudioManager audioManager;
    private AudioFocusRequest audioFocusRequest;
    
    // MediaSession 组件
    private MediaSessionCompat mediaSession;
    private MediaPlayer mediaPlayer;
    private final IBinder binder = new PlayerBinder();
    
    // 播放状态
    private boolean isPrepared = false;
    private boolean hasAudioFocus = false;
    private int currentState = PlaybackStateCompat.STATE_NONE;
    private boolean resumeAfterFocusLoss = false; // 焦点恢复后是否自动续播

    public class PlayerBinder extends Binder {
        public AudioFocusPlayerService getService() {
            return AudioFocusPlayerService.this;
        }
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "服务创建");
        
        initAudioManager();
        initMediaSession();
        initMediaPlayer();
    }

    /**
     * 初始化音频管理器
     */
    private void initAudioManager() {
        audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
        
        // 创建音频焦点请求(Android O+)
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            AudioAttributes attributes = new AudioAttributes.Builder()
                .setUsage(AudioAttributes.USAGE_MEDIA)
                .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
                .build();
            
            audioFocusRequest = new AudioFocusRequest.Builder(AudioManager.AUDIOFOCUS_GAIN)
                .setAudioAttributes(attributes)
                .setAcceptsDelayedFocusGain(false)
                .setWillPauseWhenDucked(true)
                .setOnAudioFocusChangeListener(this)
                .build();
        }
    }

    /**
     * 初始化 MediaSession
     */
    private void initMediaSession() {
        mediaSession = new MediaSessionCompat(this, "AudioFocusPlayerSession");
        mediaSession.setFlags(
            MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | 
            MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
        );
        
        mediaSession.setCallback(new MediaSessionCompat.Callback() {
            @Override
            public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
                KeyEvent keyEvent = mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
                if (keyEvent != null && keyEvent.getAction() == KeyEvent.ACTION_DOWN) {
                    return handleKeyEvent(keyEvent);
                }
                return super.onMediaButtonEvent(mediaButtonIntent);
            }
            
            @Override
            public void onPlay() {
                if (requestAudioFocus()) {
                    playInternal();
                }
            }

            @Override
            public void onPause() {
                pauseInternal();
            }

            @Override
            public void onStop() {
                stopInternal();
                abandonAudioFocus();
            }

            @Override
            public void onSeekTo(long pos) {
                seekTo(pos);
            }
        });
        
        // 必须激活会话才能接收方控事件
        mediaSession.setActive(true);
        updatePlaybackState(PlaybackStateCompat.STATE_NONE);
    }

    /**
     * 初始化 MediaPlayer
     */
    private void initMediaPlayer() {
        mediaPlayer = new MediaPlayer();
        
        AudioAttributes attributes = new AudioAttributes.Builder()
            .setUsage(AudioAttributes.USAGE_MEDIA)
            .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
            .build();
        mediaPlayer.setAudioAttributes(attributes);
        
        mediaPlayer.setOnPreparedListener(mp -> {
            isPrepared = true;
            Log.d(TAG, "播放器准备完成");
            updatePlaybackState(PlaybackStateCompat.STATE_PAUSED);
        });

        mediaPlayer.setOnCompletionListener(mp -> {
            Log.d(TAG, "播放完成");
            updatePlaybackState(PlaybackStateCompat.STATE_PAUSED);
        });

        mediaPlayer.setOnErrorListener((mp, what, extra) -> {
            Log.e(TAG, "播放错误: " + what + ", " + extra);
            updatePlaybackState(PlaybackStateCompat.STATE_ERROR);
            abandonAudioFocus();
            return true;
        });
    }

    // ==================== 音频焦点管理 ====================

    /**
     * 申请音频焦点
     */
    private boolean requestAudioFocus() {
        if (hasAudioFocus) {
            return true;
        }
        
        int result;
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            result = audioManager.requestAudioFocus(audioFocusRequest);
        } else {
            // 兼容旧版本
            result = audioManager.requestAudioFocus(
                this,
                AudioManager.STREAM_MUSIC,
                AudioManager.AUDIOFOCUS_GAIN
            );
        }
        
        hasAudioFocus = (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
        
        if (hasAudioFocus) {
            Log.d(TAG, "成功获得音频焦点");
        } else {
            Log.w(TAG, "获取音频焦点失败");
        }
        
        return hasAudioFocus;
    }

    /**
     * 释放音频焦点
     */
    private void abandonAudioFocus() {
        if (hasAudioFocus) {
            if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                audioManager.abandonAudioFocusRequest(audioFocusRequest);
            } else {
                audioManager.abandonAudioFocus(this);
            }
            hasAudioFocus = false;
            Log.d(TAG, "已释放音频焦点");
        }
    }

    /**
     * 音频焦点变化回调
     */
    @Override
    public void onAudioFocusChange(int focusChange) {
        Log.d(TAG, "音频焦点变化: " + getFocusChangeName(focusChange));
        
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN:
                // 获得焦点:恢复播放或音量
                handleFocusGain();
                break;
                
            case AudioManager.AUDIOFOCUS_LOSS:
                // 永久失去焦点:停止播放
                handleFocusLoss();
                break;
                
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                // 暂时失去焦点:暂停播放
                handleTransientFocusLoss();
                break;
                
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                // 允许降音:降低音量
                // 通常音频框架配置降音策略,APP无需处理
                handleDuckingFocusLoss();
                break;
        }
    }

    private void handleFocusGain() {
        hasAudioFocus = true;
        
        if (resumeAfterFocusLoss && isPrepared) {
            // 之前因焦点丢失而暂停,现在恢复播放
            mediaPlayer.start();
            updatePlaybackState(PlaybackStateCompat.STATE_PLAYING);
            resumeAfterFocusLoss = false;
        } else {
            // 恢复正常音量
            mediaPlayer.setVolume(1.0f, 1.0f);
        }
    }

    private void handleFocusLoss() {
        hasAudioFocus = false;
        stopInternal();
        abandonAudioFocus();
    }

    private void handleTransientFocusLoss() {
        if (isPrepared && mediaPlayer.isPlaying()) {
            mediaPlayer.pause();
            resumeAfterFocusLoss = true;
            updatePlaybackState(PlaybackStateCompat.STATE_PAUSED);
        }
    }

    private void handleDuckingFocusLoss() {
        if (isPrepared) {
            // 降低音量到30%
            mediaPlayer.setVolume(0.3f, 0.3f);
        }
    }

    private String getFocusChangeName(int focusChange) {
        switch (focusChange) {
            case AudioManager.AUDIOFOCUS_GAIN: return "GAIN";
            case AudioManager.AUDIOFOCUS_LOSS: return "LOSS";
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: return "LOSS_TRANSIENT";
            case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK: return "CAN_DUCK";
            default: return "UNKNOWN";
        }
    }

    // ==================== 播放控制 ====================

    /**
     * 加载媒体文件
     */
    public void loadMedia(Uri uri, String title, String artist) {
        try {
            resetMediaPlayer();
            
            mediaPlayer.setDataSource(this, uri);
            mediaPlayer.prepareAsync();
            
            // 更新元数据
            MediaMetadataCompat metadata = new MediaMetadataCompat.Builder()
                .putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
                .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
                .build();
            mediaSession.setMetadata(metadata);
            
        } catch (IOException e) {
            Log.e(TAG, "加载媒体失败: " + e.getMessage());
            updatePlaybackState(PlaybackStateCompat.STATE_ERROR);
        }
    }

     /**
     * 处理物理按键事件(方向盘/耳机按键)
     */
    private boolean handleKeyEvent(KeyEvent keyEvent) {
        int keyCode = keyEvent.getKeyCode();
        Log.d(TAG, "keyCode: " + KeyEvent.keyCodeToString(keyCode));
        
        switch (keyCode) {
            case KeyEvent.KEYCODE_MEDIA_PLAY:
                play();
                return true;
                
            case KeyEvent.KEYCODE_MEDIA_PAUSE:
                pause();
                return true;
                
            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                togglePlayPause();
                return true;
                
            case KeyEvent.KEYCODE_MEDIA_STOP:
                stop();
                return true;
                
            case KeyEvent.KEYCODE_MEDIA_NEXT:
                skipToNext();
                return true;
                
            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                skipToPrevious();
                return true;
                
            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
                fastForward();
                return true;
                
            case KeyEvent.KEYCODE_MEDIA_REWIND:
                rewind();
                return true;
                
            default:
                return false;
        }
    }
    
    /**
     * 内部播放方法(已检查音频焦点)
     */
    private void playInternal() {
        if (!isPrepared) {
            Log.w(TAG, "播放器未准备就绪");
            return;
        }
        
        try {
            mediaPlayer.start();
            updatePlaybackState(PlaybackStateCompat.STATE_PLAYING);
        } catch (IllegalStateException e) {
            Log.e(TAG, "播放失败: " + e.getMessage());
        }
    }

    /**
     * 公开播放方法
     */
    public void play() {
        if (requestAudioFocus()) {
            playInternal();
        }
    }

    /**
     * 内部暂停方法
     */
    private void pauseInternal() {
        if (!isPrepared || !mediaPlayer.isPlaying()) {
            return;
        }
        
        mediaPlayer.pause();
        updatePlaybackState(PlaybackStateCompat.STATE_PAUSED);
    }

    /**
     * 公开暂停方法
     */
    public void pause() {
        pauseInternal();
        // 注意:暂停时不释放音频焦点,以便快速恢复
    }

    /**
     * 停止播放
     */
    private void stopInternal() {
        if (!isPrepared) {
            return;
        }
        
        mediaPlayer.stop();
        resetMediaPlayer();
        updatePlaybackState(PlaybackStateCompat.STATE_STOPPED);
    }

    public void stop() {
        stopInternal();
        abandonAudioFocus();
    }

    public void seekTo(long positionMs) {
        if (!isPrepared) return;
        
        try {
            mediaPlayer.seekTo((int) positionMs);
        } catch (IllegalStateException e) {
            Log.e(TAG, "定位失败: " + e.getMessage());
        }
    }

    // ==================== 状态更新 ====================

    private void updatePlaybackState(int state) {
        currentState = state;
        
        long position = isPrepared ? mediaPlayer.getCurrentPosition() : 0;
        
        PlaybackStateCompat.Builder builder = new PlaybackStateCompat.Builder()
            .setActions(getAvailableActions())
            .setState(state, position, 1.0f);
        
        if (state == PlaybackStateCompat.STATE_ERROR) {
            builder.setErrorMessage("播放错误");
        }
        
        mediaSession.setPlaybackState(builder.build());
        
        Log.d(TAG, "播放状态: " + getStateName(state) + 
              ", 焦点状态: " + (hasAudioFocus ? "有焦点" : "无焦点"));
    }

    private long getAvailableActions() {
        long actions = PlaybackStateCompat.ACTION_PLAY |
                      PlaybackStateCompat.ACTION_PAUSE |
                      PlaybackStateCompat.ACTION_STOP |
                      PlaybackStateCompat.ACTION_SEEK_TO;
        
        if (currentState == PlaybackStateCompat.STATE_PLAYING) {
            actions |= PlaybackStateCompat.ACTION_PAUSE;
        } else if (currentState == PlaybackStateCompat.STATE_PAUSED ||
                  currentState == PlaybackStateCompat.STATE_STOPPED) {
            actions |= PlaybackStateCompat.ACTION_PLAY;
        }
        
        return actions;
    }

    private void resetMediaPlayer() {
        if (mediaPlayer != null) {
            mediaPlayer.reset();
            isPrepared = false;
        }
    }

    private String getStateName(int state) {
        switch (state) {
            case PlaybackStateCompat.STATE_PLAYING: return "播放中";
            case PlaybackStateCompat.STATE_PAUSED: return "已暂停";
            case PlaybackStateCompat.STATE_STOPPED: return "已停止";
            case PlaybackStateCompat.STATE_ERROR: return "错误";
            default: return "未知";
        }
    }

    // ==================== 服务生命周期 ====================

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            mediaSession.handleMediaKeyEvent(intent);
        }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "服务销毁");
        
        stopInternal();
        abandonAudioFocus();
        
        if (mediaPlayer != null) {
            mediaPlayer.release();
        }
        if (mediaSession != null) {
            mediaSession.release();
        }
    }

    // ==================== 公共方法 ====================

    public MediaSessionCompat.Token getSessionToken() {
        return mediaSession.getSessionToken();
    }

    public int getCurrentState() {
        return currentState;
    }

    public boolean hasAudioFocus() {
        return hasAudioFocus;
    }
}

客户端 (MusicPlayerActivity.java)

复制代码
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaControllerCompat;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class MusicPlayerActivity extends AppCompatActivity {
    private static final String TAG = "MusicPlayerActivity";
    
    private TextView tvStatus, tvTitle, tvArtist, tvPosition;
    private Button btnPlayPause, btnStop, btnNext, btnPrev;
    
    private MusicService musicService;
    private MediaControllerCompat mediaController;
    private MediaControllerCallback controllerCallback;
    private Handler uiHandler = new Handler(Looper.getMainLooper());
    
    private boolean isBound = false;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MusicService.LocalBinder binder = (MusicService.LocalBinder) service;
            musicService = binder.getService();
            isBound = true;
            
            setupMediaController();
             // 加载并播放音乐
            Uri musicUri = Uri.parse("file:///sdcard/Music/test.mp3");
            musicService.loadMedia(musicUri, "测试歌曲", "测试歌手");
            musicService.play();
            updateUI();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            musicService = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);
        
        initViews();
        bindMusicService();
    }

    private void initViews() {
        tvStatus = findViewById(R.id.tv_status);
        tvTitle = findViewById(R.id.tv_title);
        tvArtist = findViewById(R.id.tv_artist);
        tvPosition = findViewById(R.id.tv_position);
        
        btnPlayPause = findViewById(R.id.btn_play_pause);
        btnStop = findViewById(R.id.btn_stop);
        btnNext = findViewById(R.id.btn_next);
        btnPrev = findViewById(R.id.btn_prev);

        btnPlayPause.setOnClickListener(v -> togglePlayPause());
        btnStop.setOnClickListener(v -> stop());
        btnNext.setOnClickListener(v -> skipToNext());
        btnPrev.setOnClickListener(v -> skipToPrevious());
    }

    // 播放控制方法
    private void togglePlayPause() {
        if (mediaController == null) return;
        
        PlaybackStateCompat state = mediaController.getPlaybackState();
        if (state != null) {
            if (state.getState() == PlaybackStateCompat.STATE_PLAYING) {
                mediaController.getTransportControls().pause();
            } else {
                mediaController.getTransportControls().play();
            }
        }
    }

    private void stop() {
        if (mediaController != null) {
            mediaController.getTransportControls().stop();
        }
    }

    private void skipToNext() {
        if (mediaController != null) {
            mediaController.getTransportControls().skipToNext();
        }
    }

    private void skipToPrevious() {
        if (mediaController != null) {
            mediaController.getTransportControls().skipToPrevious();
        }
    }

    private void bindMusicService() {
        Intent intent = new Intent(this, MusicService.class);
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);
    }

    private void setupMediaController() {
        try {
            MediaSessionCompat.Token token = musicService.getSessionToken();
            mediaController = new MediaControllerCompat(this, token);
            MediaControllerCompat.setMediaController(this, mediaController);
            
            controllerCallback = new MediaControllerCallback();
            mediaController.registerCallback(controllerCallback);
            
        } catch (Exception e) {
            Log.e(TAG, "Failed to setup media controller: " + e.getMessage());
        }
    }
    
    // UI 更新方法
    private void updateUI() {
        if (musicService == null) return;
        
        uiHandler.post(() -> {
            // 更新播放状态
            int state = musicService.getCurrentState();
            String statusText = "";
            switch (state) {
                case PlaybackStateCompat.STATE_PLAYING:
                    statusText = "正在播放";
                    btnPlayPause.setText("暂停");
                    break;
                case PlaybackStateCompat.STATE_PAUSED:
                    statusText = "已暂停";
                    btnPlayPause.setText("播放");
                    break;
                case PlaybackStateCompat.STATE_STOPPED:
                    statusText = "已停止";
                    btnPlayPause.setText("播放");
                    break;
                case PlaybackStateCompat.STATE_ERROR:
                    statusText = "播放错误";
                    break;
            }
            tvStatus.setText(statusText);
            
            // 更新进度
            long position = musicService.getCurrentPosition();
            tvPosition.setText(formatDuration(position));
        });
    }

    private String formatDuration(long millis) {
        long seconds = millis / 1000;
        long minutes = seconds / 60;
        seconds = seconds % 60;
        return String.format("%02d:%02d", minutes, seconds);
    }

    // MediaController 回调类
    private class MediaControllerCallback extends MediaControllerCompat.Callback {
        @Override
        public void onPlaybackStateChanged(PlaybackStateCompat state) {
            super.onPlaybackStateChanged(state);
            Log.d(TAG, "Playback state changed: " + state.getState());
            updateUI();
        }

        @Override
        public void onMetadataChanged(MediaMetadataCompat metadata) {
            super.onMetadataChanged(metadata);
            if (metadata != null) {
                uiHandler.post(() -> {
                    String title = metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE);
                    String artist = metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST);
                    
                    tvTitle.setText(title != null ? title : "未知标题");
                    tvArtist.setText(artist != null ? artist : "未知歌手");
                });
            }
        }

        @Override
        public void onSessionDestroyed() {
            super.onSessionDestroyed();
            Log.w(TAG, "Session destroyed");
            finish();
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (mediaController != null && controllerCallback != null) {
            mediaController.registerCallback(controllerCallback);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (mediaController != null && controllerCallback != null) {
            mediaController.unregisterCallback(controllerCallback);
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (isBound) {
            unbindService(serviceConnection);
        }
    }
}

布局文件 (activity_player.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:padding="16dp">

    <TextView
        android:id="@+id/tv_status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="未连接"
        android:textSize="18sp"
        android:textStyle="bold" />

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="标题"
        android:textSize="16sp" />

    <TextView
        android:id="@+id/tv_artist"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="歌手"
        android:textSize="14sp" />

    <TextView
        android:id="@+id/tv_position"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:text="00:00"
        android:textSize="12sp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="24dp"
        android:orientation="horizontal">

        <Button
            android:id="@+id/btn_prev"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="上一首" />

        <Button
            android:id="@+id/btn_play_pause"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="播放/暂停" />

        <Button
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止" />

        <Button
            android:id="@+id/btn_next"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="下一首" />
    </LinearLayout>

</LinearLayout>

配置文件(AndroidManifest.xml)

复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.musicplayer">

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <application>
        <!-- 播放服务 -->
        <service
            android:name=".MusicPlayerService"
            android:foregroundServiceType="mediaPlayback"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MEDIA_BUTTON" />
            </intent-filter>
        </service>

        <!-- 播放界面 -->
        <activity android:name=".MusicPlayerActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
            </intent-filter>
        </activity>
    </application>

</manifest>
相关推荐
私人珍藏库5 小时前
[Android] 蓝叠模拟器工具箱v1.1
android·智能手机·app·工具·软件·多功能
云霄IT7 小时前
安卓开发之java转dex再转smali
android·java·python
XiaoLeisj7 小时前
Android 短视频项目实战:从用户中心页与沉浸式登录,到验证码鉴权、用户信息持久化和 EventBus 登录态同步
android·webview·eventbus·countdowntimer·token 加密·键值对存储 sp·封装toast/加载 ui
JJay.9 小时前
Android BLE 扫描连接与收发消息实战
android
fly spider9 小时前
MySQL索引篇
android·数据库·mysql
xinhuanjieyi10 小时前
php setplayersjson实现类型转换和文件锁定机制应对高并发
android·开发语言·php
533_10 小时前
[vxe-table] 表头:点击出现输入框
android·java·javascript
邹阿涛涛涛涛涛涛10 小时前
Jetpack Compose Modifier 深度解析:从链式调用到 Modifier.Node
android
jinanwuhuaguo11 小时前
OpenClaw 2026年4月升级大系深度解读剖析:从“架构重塑”到“信任内建”的范式跃迁
android·开发语言·人工智能·架构·kotlin·openclaw