问题描述:
手机连接蓝牙耳机(SAMSUNG耳机),无法用耳机切换上一首(只能切换下一首)
重现问题:
这个问题因为重现概率是100%,所以我借了一只蓝牙耳机,从设置的蓝牙进入,点击连接蓝牙耳机,切换下一首,手机能正常插入下一首。点击上一首,问题来了,确实是不能播放上一首,具体的表现是重新播放此首音乐。
初步分析:
根据问题描述,初步分析:
- 可能是蓝牙耳机切换下一首按键命令有问题,可能按下切换下一首按键,发出的是重播的命令。
- 也有可能是按键命令本身正确,但是处理切换下一首按键的代码逻辑本身的问题
问题解决:
首先,确认蓝牙耳机按键是否正常。
我用自己的小米手机测试了此蓝牙耳机,发现在小米手机上蓝牙耳机的切换上一首和切换下一首功能是正常的,那么基本上排除了第一个蓝牙耳机切换下一首按键命令问题。
下面就只有第二个原因,
我首先查看了log,但是没有发现有效的log信息。
再在网上查了一资料,找到了一个感觉有用的东西:
Android 接收蓝牙耳机按键操作
http://blog.csdn.net/lihongyu65085/article/details/39853669
我选择关键字蓝牙耳机下一首按键来搜索:
KeyEvent.KEYCODE_MEDIA_NEXT
查找到代码:
……/app/Music/src/com/android/music/MediaButtonIntentReceiver.java:91: case KeyEvent.KEYCODE_MEDIA_NEXT:
好家伙,确实有线索。我们打开此文件:
public void onReceive(Context context, Intent intent) {
String intentAction = intent.getAction();
if (AudioManager.ACTION_AUDIO_BECOMING_NOISY.equals(intentAction)) {
Intent i = new Intent(context, MediaPlaybackService.class);
i.setAction(MediaPlaybackService.SERVICECMD);
i.putExtra(MediaPlaybackService.CMDNAME, MediaPlaybackService.CMDPAUSE);
context.startService(i);
} else if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
KeyEvent event = (KeyEvent)
intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
if (event == null) {
return;
}
int keycode = event.getKeyCode();
int action = event.getAction();
long eventtime = event.getEventTime();
// single quick press: pause/resume.
// double press: next track
// long press: start auto-shuffle mode.
String command = null;
switch (keycode) {
case KeyEvent.KEYCODE_MEDIA_STOP:
command = MediaPlaybackService.CMDSTOP;
break;
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
command = MediaPlaybackService.CMDTOGGLEPAUSE;
break;
case KeyEvent.KEYCODE_MEDIA_NEXT:
command = MediaPlaybackService.CMDNEXT;
break;
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
command = MediaPlaybackService.CMDPREVIOUS;
break;
case KeyEvent.KEYCODE_MEDIA_PAUSE:
command = MediaPlaybackService.CMDPAUSE;
break;
case KeyEvent.KEYCODE_MEDIA_PLAY:
command = MediaPlaybackService.CMDPLAY;
break;
}
此代码,我们可以知道,MediaButtonIntentReceiver主要接受广播AudioManager.ACTION_AUDIO_BECOMING_NOISY和Intent.ACTION_MEDIA_BUTTON。而我们的蓝牙按键就是发送广播Intent.ACTION_MEDIA_BUTTON。并且,我们可以看到我们的按键事件:
KeyEvent.KEYCODE_MEDIA_STOP —停止
KeyEvent.KEYCODE_MEDIA_PAUSE —暂停
KeyEvent.KEYCODE_MEDIA_NEXT —下一首
MediaPlaybackService.CMDPREVIOUS —上一首
KeyEvent.KEYCODE_MEDIA_PLAY —播放
再往下看:
// only consider the first event in a sequence, not the repeat events,
// so that we don't trigger in cases where the first event went to
// a different app (e.g. when the user ends a phone call by
// long pressing the headset button)
// The service may or may not be running, but we need to send it
// a command.
Intent i = new Intent(context, MediaPlaybackService.class);
i.setAction(MediaPlaybackService.SERVICECMD);
if (keycode == KeyEvent.KEYCODE_HEADSETHOOK &&eventtime - mLastClickTime < 300) {
i.putExtra(MediaPlaybackService.CMDNAME, MediaPlaybackService.CMDNEXT);
context.startService(i);
mLastClickTime = 0;
} else {
i.putExtra(MediaPlaybackService.CMDNAME, command);
context.startService(i);
mLastClickTime = eventtime;
}
从代码,我们可以看出,启动一个服务:MediaPlaybackService,并将按键命令发送过去。
我们再打开MediaPlaybackService:
public int onStartCommand(Intent intent, int flags, int startId) {
………..
String action = intent.getAction();
String cmd = intent.getStringExtra("command");
if (CMDNEXT.equals(cmd) || NEXT_ACTION.equals(action)) {
gotoNext(true);
} else if (CMDPREVIOUS.equals(cmd) || PREVIOUS_ACTION.equals(action)) {
if (position() < 2000) {
prev();
} else {
seek(0);
play();
}
从代码,我们可以看出:当按键命令是上一首时,
seek(0);
play();
这段代码,非常明显,就是导致不能播放上一首的原因所在。
问题找到了,解决就比较简单了:
//JABALCOA-139 prev music by bluetooth headset 20151208 start
//seek(0);
//play();
prev();
//JABALCOA-139 prev music by bluetooth headset 20151208 end
总结:
我们再来看MediaButtonIntentReceiver广播监听器:
其主要监听如下二个广播:
<receiver android:name="com.android.music.MediaButtonIntentReceiver">
<intent-filter>
<action android:name="android.intent.action.MEDIA_BUTTON" />
<action android:name="android.media.AUDIO_BECOMING_NOISY" />
</intent-filter>
</receiver>
监听按键事件有:
KeyEvent.KEYCODE_MEDIA_STOP:
KeyEvent.KEYCODE_HEADSETHOOK:
KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
KeyEvent.KEYCODE_MEDIA_NEXT:
KeyEvent.KEYCODE_MEDIA_PREVIOUS:
KeyEvent.KEYCODE_MEDIA_PAUSE:
KeyEvent.KEYCODE_MEDIA_PLAY:
当监听到按键事件后,再启动服务:MediaPlaybackService
<service android:name="com.android.music.MediaPlaybackService"
android:exported="false" />
在onStartCommand方法中判断按键事件,再进行相应的操作。