Android 注冊媒體按鍵監聽(MediaSession 兼容 Android 5.0+)

雷子游 7年前發布 | 13K 次閱讀 安卓開發 Android開發 移動開發

使用場景描述:

應用中需要播放音樂的時候,通常有個令人捉急的問題就是媒體焦點;假如同時用 QQ音樂 與自己的應用同時播放音樂的時候,媒體焦點到底花落誰家,誰才能響應這次媒體按鍵;這就要看誰最后申請了這個焦點

系統中可能有多個應用程序在播放音頻,而媒體按鍵只有一個,只會響應在最后一個獲取了媒體焦點的應用;

先來看看怎么請求焦點,注冊媒體按鍵監聽(在你的播放器播放的時候調用以下代碼注冊就可以將媒體焦點搶過來);

audioManager = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
mComponentName = new ComponentName(mContext.getPackageName(),MediaButtonIntentReceiver.class.getName());  
int result = audioManager
            .requestAudioFocus(new MyOnAudioFocusChangeListener(), 
                    AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == result) {//到這一步,焦點已經請求成功了
        if (android.os.Build.VERSION.SDK_INT >= 21) {
            //注冊媒體按鍵 API 21+(Android 5.0)
            setMediaButtonEvent();
        } else {
            //注冊媒體按鍵 API 21 以下, 通常的做法 
            audioManager.registerMediaButtonEventReceiver(mComponentName);
        }
}

媒體焦點變化監聽(看上面代碼先請求焦點,再根據 API 等級分別注冊監聽,所以這里焦點變化監聽是通用的)

class MyOnAudioFocusChangeListener implements AudioManager.OnAudioFocusChangeListener {
        @Override
        public void onAudioFocusChange(int focusChange) {
            switch(focusChange) {

                case AudioManager.AUDIOFOCUS_GAIN:
                    // 重新獲得焦點,  可做恢復播放,恢復后臺音量的操作
                    break;
                case AudioManager.AUDIOFOCUS_LOSS:
                    // 永久丟失焦點除非重新主動獲取,這種情況是被其他播放器搶去了焦點,  為避免與其他播放器混音,可將音樂暫停
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
                    // 暫時丟失焦點,這種情況是被其他應用申請了短暫的焦點,可壓低后臺音量
                    break;
                case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
                    // 短暫丟失焦點,這種情況是被其他應用申請了短暫的焦點希望其他聲音能壓低音量(或者關閉聲音)凸顯這個聲音(比如短信提示音),
                    break;
            }
        }
    }

API 21 之前是通過廣播注冊的方式來獲得按鍵的通知:

public class MediaButtonIntentReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        String intentAction = intent.getAction();
        if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
            KeyEvent event = (KeyEvent) intent
                .getParcelableExtra(Intent.EXTRA_KEY_EVENT);
        if (event == null)return;
        int keycode = event.getKeyCode();
        switch (keycode) {
        case KeyEvent.KEYCODE_MEDIA_STOP:
            //CMD STOP 
            break;
        case KeyEvent.KEYCODE_HEADSETHOOK:
        case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
            //CMD TOGGLE PAUSE
            break;
        case KeyEvent.KEYCODE_MEDIA_NEXT:
            //CMD NEXT 這里處理播放器邏輯 下一曲
            break;
        case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
            //CMD PREVIOUS 這里處理播放器邏輯 上一曲
            break;
        case KeyEvent.KEYCODE_MEDIA_PAUSE:
            //CMD PAUSE 這里處理播放器邏輯 暫停
            break;
        case KeyEvent.KEYCODE_MEDIA_PLAY:
            //CMD PLAY 這里處理播放器邏輯 播放
            break;
        }
    }
}

接下來看看 API 21 注冊媒體按鍵的監聽

如果 API 21+ 用以上廣播注冊的方式也是可以的監聽的,區別在于如果有別的播放器搶占了焦點你可就搶不回來了

@TargetApi(Build.VERSION_CODES.LOLLIPOP)//注意申明 API 21
private void setMediaButtonEvent() {
    session = new MediaSession(mContext, "隨便寫一串 tag 就行");
    session.setCallback(new MediaSession.Callback() {
        @Override
        public void onPlay() {
            super.onPlay();
            //這里處理播放器邏輯 播放
            updatePlaybackState(true);//播放暫停更新控制中心播放狀態
        }

        @Override
        public void onPause() {
            super.onPause();
            //這里處理播放器邏輯 暫停
            updatePlaybackState(false);//播放暫停更新控制中心播放狀態
        }

        @Override
        public void onSkipToNext() {
            super.onSkipToNext();
            //CMD NEXT 這里處理播放器邏輯 下一曲
        }

        @Override
        public void onSkipToPrevious() {
            super.onSkipToPrevious();
            //這里處理播放器邏輯 上一曲
        }
    });
}

播放暫停更新控制中心的播放狀態邏輯(會在手機鎖屏后顯示歌曲的播放狀態):

/*
    * update mediaCenter state
    */
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void updatePlaybackState(boolean isPlaying) {
    PlaybackState.Builder stateBuilder = new PlaybackState.Builder()
            .setActions(PlaybackState.ACTION_PLAY
                    | PlaybackState.ACTION_PLAY_PAUSE
                    | PlaybackState.ACTION_PLAY_FROM_MEDIA_ID
                    | PlaybackState.ACTION_PAUSE
                    | PlaybackState.ACTION_SKIP_TO_NEXT
                    | PlaybackState.ACTION_SKIP_TO_PREVIOUS
                    | PlaybackState.CONTENTS_FILE_DESCRIPTOR);
    if (isPlaying) {
        stateBuilder.setState(PlaybackState.STATE_PLAYING,
                PlaybackState.PLAYBACK_POSITION_UNKNOWN,
                SystemClock.elapsedRealtime());
    } else {
        stateBuilder.setState(PlaybackState.STATE_PAUSED,
                PlaybackState.PLAYBACK_POSITION_UNKNOWN,
                SystemClock.elapsedRealtime());
    }
    session.setPlaybackState(stateBuilder.build());
}

切換歌曲更新控制中心的播放歌曲的信息(會在手機鎖屏后顯示歌名,歌手等信息)

//點擊播放 注冊監聽后調用此方法才能將媒體焦點搶過來
 @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public void updateMediaCenterInfo(String title, String artist) {
        if (session == null) return;
        MediaMetadata.Builder metadataBuilder = new MediaMetadata.Builder();
        metadataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE, title);//歌曲名
        metadataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST, artist);//歌手
        session.setMetadata(metadataBuilder.build());
        updatePlaybackState(true);
    }

最后不需要使用媒體焦點記得注銷:

@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void unRegisterMediaButton(){
    if(audioManager==null|mComponentName==null)return;
    audioManager.abandonAudioFocus(onAudioFocusChangeListener);
    audioManager.unregisterMediaButtonEventReceiver(mComponentName);
    if (session != null) {
        session.setCallback(null);
        session.setActive(false);
        session.release();
    }
}

MediaSession:

用 MediaSession 的方式注冊監聽,不做處理在一些手機鎖屏后就會顯示播放界面,并能夠控制上下曲,暫停/播放

官方文檔的說明:

允許與媒體控制器,音量鍵,媒體按鈕和傳輸控件進行交互。

當應用程序想要發布媒體播放信息或處理媒體密鑰時,應創建MediaSession。 一般來說,應用程序只需要一個會話進行所有播放,雖然可以創建多個會話以提供更精細的媒體控制。

一旦會話被創建,會話的所有者可以將其會話令牌傳遞給其他進程,以允許他們創建一個MediaController來與會話交互。

要接收命令,媒體鍵和其他事件,必須使用setCallback(Callback)和setActive(true)設置MediaSession.Callback。

當應用程序完成播放時,必須調用release()清除會話并通知任何控制器。

MediaSession對象是線程安全的。

 

來自:http://www.jianshu.com/p/b52754c50f89

 

 本文由用戶 雷子游 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!