安卓接收耳机按键控制音频播放

全栈工程师开发手册 (作者:栾鹏)
安卓教程全解

如今连接移动设备的耳机上基本都有按键,来控制音频的播放,暂停,下一首,上一首,或电话的拨打,视频的播放等功能。而外置媒体按键的按下,安卓系统接收到这个信号以后会向系统所有app发送一个媒体按键的广播事件。app注册接收按键事件来进行相应的操作。而且接收这一事件的广播接收器必须在manifest中注册

另外,安卓只允许当前时刻只有一个app能获取音频焦点。所以可以控制当有别的播放器打开后,自己的app就可以会失去焦点,通过节点事件调整自己app的播放功能。

由于外接媒体按键的广播接收器只能在maifest中注册,所以只能通过先用自定义接收器接收这一事件,再向本地app发送一个代表这一事件的间接事件。目标窗口接收间接事件,再进行相应的处理。

首先要在manifest中注册媒体按键的广播接收器

设备带有播放、停止、暂停的按键,按下、弹起时系统会广播一个带有ACTION_MEDIA_BUTTON动作的intent

<receiver android:name="com.lp.app.media.MediaControlReceiver">
	<intent-filter>
		<action android:name="android.intent.action.MEDIA_BUTTON"/>
	</intent-filter>
</receiver>

定义广播接收器,接收媒体按键广播事件,并向本地app广播间接事件。

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;

//设备按键广播接收器,接收到intent后,判断是否是设备按键广播,然后广播给音频播放activity。因为这个监听器只能在mainfest中注册
public class MediaControlReceiver extends BroadcastReceiver {
  
  public static final String ACTION_MEDIA_BUTTON = "com.lp.app.ACTION_MEDIA_BUTTON";
  
  @Override
  public void onReceive(Context context, Intent intent) {
    if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
      Intent internalIntent = new Intent(ACTION_MEDIA_BUTTON);  
      
      internalIntent.putExtras(intent.getExtras());   //将数据也传递给目标窗口,播放、暂停、声音调大、声音调低。按下弹起都会发起广播
      context.sendBroadcast(internalIntent);
      Log.v("媒体按键广播接收器", "接收到外接媒体按键"+internalIntent.getAction()+"发送内部广播给本app");
    }
  }
}

目标activty先进行基本的音频播放配置。准备接收间接媒体按键事件

  private MediaPlayer mediaPlayer;
  
//配置音频播放
  private void configureAudio() {
    try {      
      mediaPlayer = new MediaPlayer();
      mediaPlayer.setDataSource("/sdcard/test.mp3");
      mediaPlayer.prepare();
      
    } catch (IllegalArgumentException e) {
      Log.d("媒体按键广播接收窗口", "Illegal Argument Exception: " + e.getMessage());
    } catch (SecurityException e) {
      Log.d("媒体按键广播接收窗口", "Security Exception: " + e.getMessage());
    } catch (IllegalStateException e) {
      Log.d("媒体按键广播接收窗口", "Illegal State Exception: " + e.getMessage());
    } catch (IOException e) {
      Log.d("媒体按键广播接收窗口", "IO Exception: " + e.getMessage());
    }
  }

目标activity中接收间接广播事件。同时可以注册耳机拔出事件的广播接收器

private ActivityMediaControlReceiver activityMediaControlReceiver;   //间接广播事件

@Override
  protected void onResume() {
    super.onResume();
    
    //媒体按键事件的广播接收器,在mainfest中注册,接收器接收到事件后统一在app内广播一个新的事件表征媒体按键的事件,所有接收此事件的窗口再去处理。
    AudioManager am =(AudioManager)getSystemService(Context.AUDIO_SERVICE);   //获取声量管理服务
    ComponentName component = new ComponentName(this, MediaControlReceiver.class);

    am.registerMediaButtonEventReceiver(component);  //将接收处理程序注册为媒体按键按下动作的唯一处理程序。这样屏蔽多个app的同时处理。不过不一定有权限

    // 注册一个本地intent receiver,用于接收在manifest文件中注册的receiver
    //媒体按键按下动作
    activityMediaControlReceiver = new ActivityMediaControlReceiver();
    IntentFilter filter = new IntentFilter(MediaControlReceiver.ACTION_MEDIA_BUTTON);
    
    registerReceiver(activityMediaControlReceiver, filter);
    Log.v("媒体按键监听", "注册了间接媒体按键监听器");
    //拔出耳机的广播接收器
    IntentFilter noiseFilter = new IntentFilter(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
    registerReceiver(new NoisyAudioStreamReceiver(), noiseFilter);
    Log.v("媒体按键监听", "注册了耳机拔出监听器");
  }

其中间接广播接收器的定义如下,主要完成音频的播放、暂停、停止、音量调整、上一首、下一首。

//媒体按键BroadcastReceiver的实现,接收设备按键,控制音频播放
  public class ActivityMediaControlReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      if (MediaControlReceiver.ACTION_MEDIA_BUTTON.equals(intent.getAction())) {
        KeyEvent event =(KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);   //
        if(event.getAction()!=KeyEvent.ACTION_UP)   //这里只处理弹起事件
        	return;
        Log.v("简介媒体按键监听器", "接收到按键"+event);
        switch (event.getKeyCode()) {
          case (KeyEvent.KEYCODE_HEADSETHOOK) :  //接听/挂断 电话,播放/暂停音乐视频  多功能按键
        	  if (mediaPlayer.isPlaying())
              {
              	pause();
            	Log.v("音频播放", "暂停");	
              }
              else
              {
              	play();
            	Log.v("音频播放", "播放");	
              }
              break;
          case (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE) :    //如果是暂停或暂停键就控制音频播放或暂停
            if (mediaPlayer.isPlaying())
            {
            	pause();
          		Log.v("音频播放", "暂停");	
            }
            else
            {
            	play();
          		Log.v("音频播放", "播放");	
            }
            break;
          case (KeyEvent.KEYCODE_MEDIA_PLAY) :    //如果是播放键,就播放
            play(); break;
          case (KeyEvent.KEYCODE_MEDIA_PAUSE) :  //如果是暂停键,就暂停
            pause(); break;
          case (KeyEvent.KEYCODE_MEDIA_NEXT) :   //如果是下一首,就跳转到下一首
            skip(); break;
          case (KeyEvent.KEYCODE_MEDIA_PREVIOUS) :   //如果是上一首,就跳转到上一首
            previous(); break;
          case (KeyEvent.KEYCODE_MEDIA_STOP) :    //如果是停止键,就停止
            stop(); break;
          default: break;
        }
      }
    }
  }

耳机拔出事件的广播接收器如下

  //当拔出耳机时暂停播放
  private class NoisyAudioStreamReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
      if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intent.getAction())) {
        pause();
        Log.v("媒体按键监听", "耳机拔出");
      }
    }
  }

音频的播放和停止设计的音频焦点的设计。由于在安卓系统中只允许一个窗口获取当前音频焦点。所以在开始播放音频时,应该主动获取音频焦点,以便其他播放器app,停止音频播放,在丢失音频焦点时,主动放弃音频播放。

定义一个音频焦点的变更事件

 //音频焦点变化响应。在用户设备有多个媒体播放app时,非活动状态app交出媒体按键控制权。活动状态app获取媒体按键控制权
  //分为暂时丢失、丢失、永久丢失。这是安装系统的架构
  private OnAudioFocusChangeListener focusChangeListener =  new OnAudioFocusChangeListener() {
    
    public void onAudioFocusChange(int focusChange) {
    	
      AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
      Log.v("按键广播监听", "音频焦点变化"+focusChange);
      switch (focusChange) {
        case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) :   //暂时丢失,支持音量降低
          //降低音量
          mediaPlayer.setVolume(0.2f, 0.2f);
          break;
          //暂停
        case (AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) :   //丢失
          pause();
          break;
          //停止播放
        case (AudioManager.AUDIOFOCUS_LOSS) :  //永久丢失
          stop();
          ComponentName component = new ComponentName(AudioPlayerActivity.this,MediaControlReceiver.class);
          am.unregisterMediaButtonEventReceiver(component);  //注销广播响应程序
          break;

        case (AudioManager.AUDIOFOCUS_GAIN) :   //暂时丢失后,重获焦点
          //将音量恢复到正常大小,并且如果音频流已被暂停,则恢复音频流
          mediaPlayer.setVolume(1f, 1f);
          mediaPlayer.start();
          break;

        default: break;
      }
    }
  };

音频播放函数

  //请求音频焦点(音频焦点只能由一个app获取)。
  public void play() {    
    AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);

    //请求音频焦点
    int result = am.requestAudioFocus(focusChangeListener,  //(音频焦点丢失响应)
                   //使用音频流
                   AudioManager.STREAM_MUSIC,
                   //请求永久焦点
                   AudioManager.AUDIOFOCUS_GAIN);
       
    if (result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {   //如果焦点请求成功
       mediaPlayer.start();  //播放音频
    }
    
      

音频停止

//音频播放完成后,放弃音频焦点。
  public void stop() {
    mediaPlayer.stop();
    Log.v("音频播放", "放弃音频焦点");
    //放弃音频焦点
    AudioManager am = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
    am.abandonAudioFocus(focusChangeListener);
  }

音频的暂停、上一首、下一首就根据自己想要的的功能来实现

  //暂停函数,包含设置远程控制窗口
  public void pause() {
    mediaPlayer.pause();
    myRemoteControlClient.setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED);
  }
  
  //跳到下一首
  public void skip() {
    //自己实现
  }
  
  //跳到前一首
  public void previous() {
    //自己实现
  }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

腾讯AI架构师

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值