android之BroadcastReceiver应用实列(耳机按键切换下一首音乐)

当你用耳机听音乐的时候,想通过耳机上的按钮来快速切换下一首、上一首音乐是不是很方便呢!如果,你的手机有这样一个功能当然是不错的!下面来看看我是如何实现的!

对于这个功能的开发,首先要知道两点:(1)耳机按键事件如何获取(短按和长按事件);(2)如果切换上一首音乐、切换下一首音乐。所以我们先来解决这两个问题:

(1)耳机按键事件如何获取。

         首先来看看耳机按钮的事件是否有传递到上层framwork。查上耳机、连接USB,然后通过log查看工具,我们通过打印的LOG信息可以很快知道,其事件是会上传上来的。

01-01 01:07:44.524: D/WindowManager(514): interceptKeyTi keyCode=85 down=false repeatCount=0 keyguardOn=false mHomePressed=false canceled=false

01-01 01:07:47.644: D/WindowManager(514): interceptKeyTi keyCode=86 down=true repeatCount=0 keyguardOn=false mHomePressed=false canceled=false

       通过上面的LOG信息,你可以知道长按耳机上的按钮,其 keyCode=86;短按耳机上的按钮,其 keyCode=85;

      PhoneWindowManager.java里面的这方法interceptKeyBeforeQueueing就是按键时间的接口函数!

(2)如果切换上一首音乐、切换下一首音乐。

      我们知道,音乐的播放其实是运行一个service的!在Music里面,我们可以很快知道这个service其实就是:MediaPlaybackService。通过这个service 的onStartCommand方法可以知道,在onStartCommand里面其实是根据intent带回来的不同参数来控制音乐播放的,比如暂停、播放、下一首、上一首等。

上面两个问题已经解决了!接下来的问题就是如果把这两个问题关联起来!也就是耳机事件如何传递到这个music的app里面来!说的具体一点,就是耳机按键时间如果传递到这个播放音乐的service里面来!!我们知道service的直接父类其实是ContextWrapper,想对于activity,并没有实现Window.Callback, KeyEvent.Callback,这两个接口,所以更本上是无法接受这个耳机按键事件的!

        那怎么解决这个问题呢!很快想到了BroadcastReceiver,用BroadcastReceiver来接受按键事件! 在music这个app里面定义一个BroadcastReceiver,在PhoneWindowManager里面把检测到的事件发送到我们再music里面定义的BroadcastReceiver。这样问题就解决了!

      以上就是解决这个问题的基本思路!不过,一些细微的问题需要注意,下面一个个来说:

(1)在music定义的BroadcastReceiver由于是监听这个app外面的广播事件,所以只是在代码里面动态注册是不可取的!必须AndroidManifest.xml这样定义:

        <receiver android:name="com.android.music.HEADSET_key_Receiver"
        android:exported="true" >
            <intent-filter>
                <action android:name="com.android.music.HEADSET_key_input" />
            </intent-filter>
        </receiver>

这里的android:exported="true"  是必须的。

(2)在PhoneWindowManager中,检测到耳机按键事件,如下方式发送广播:

Intent intent = new Intent();
intent.putExtra("music_change", music_change);
intent.setClassName("com.android.music", "com.android.music.HEADSET_key_Receiver");//
intent.setAction("com.android.music.HEADSET_key_input");

这里最好setClassName,这样一来效率高。

(3)在PhoneWindowManager中,检测到耳机按键的事件是down事件。

(4)在PhoneWindowManager中,检测到耳机按键事件时候最好能检测一下手机是否在播放音乐:

    boolean isMusicActive() {
        final AudioManager am = (AudioManager)mContext.getSystemService(Context.AUDIO_SERVICE);
        if (am == null) {
            Log.w(TAG, "isMusicActive: couldn't get AudioManager reference");
            return false;
        }
        return am.isMusicActive();
    }

还需要检测一下在在通话过程中incall

ITelephony telephonyService = getTelephonyService();
if (telephonyService != null) {// 这里检测一下是否当前正在通常 incall
try {
if (!telephonyService.isIdle()) {
// Suppress PLAY/PAUSE toggle when phone is ringing or in-call
// to avoid music playback.

return;
}
} catch (RemoteException ex) {
Log.w(TAG, "ITelephony threw RemoteException", ex);
}
}

(5)在music定义的BroadcastReceiver中在start播放音乐的service的时候,最好能够检测一下这个service是否处于运行当中:

    private boolean isMusicServiceRunning(Context context) {
        boolean isServiceRuning = false;
        ActivityManager am = (ActivityManager)context.getSystemService(Context.ACTIVITY_SERVICE);
        final int maxServciesNum = 100;
        List<RunningServiceInfo> list = am.getRunningServices(maxServciesNum);
        for (RunningServiceInfo info : list) {
            if (MediaPlaybackService.class.getName().equals(info.service.getClassName())) {
                isServiceRuning = true;
                break;
            }
        }
      //  MusicLogUtils.d("yuyongjun", "isMusicServiceRunning " + isServiceRuning + ", Runing service num is " + list.size());
        return isServiceRuning;
    }

(6)尽管播放音乐的service是一个运行的service,但是并不代表现在正在播放音乐(比如 暂停播放等)。所以,是否正在播放音乐其实需要在这个service(MediaPlaybackService)中才能确认。所以,我们start这个service的时候需要带一个标识:

        Intent i = new Intent(context, MediaPlaybackService.class);
        i.setAction(MediaPlaybackService.SERVICECMD);
        i.putExtra(MediaPlaybackService.CMDNAME, command);
        i.putExtra(MediaPlaybackService.DELTATIME, deltaTime);
        i.putExtra(HEADSET_key_input, "true");
        context.startService(i);

上面的 i.putExtra(HEADSET_key_input, "true");用于标示是耳机按钮事件触发。所以在MediaPlaybackService的onStartCommand有如下代码判断。

// yuyongjun _start 2014-4-16  FeatureOption.BASICOM_HEADSET_KEY_CONTROL_MUSIC_CHANGE_SONG
        String HEADSET_key_input = intent.getStringExtra(HEADSET_key_Receiver.HEADSET_key_input);
        if(HEADSET_key_input != null && HEADSET_key_input.equals("true"))
        {
       
  if(false == mIsSupposedToBePlaying)
        {
        return START_STICKY;
        }
MusicLogUtils.d("yuyongjun", "onStartCommand: HEADSET_key_input");
        }// yuyongjun _end 2014-4-16

你可能感兴趣的:(android,Android开发,frameworks)