如何获取当前正在播放音乐的音乐软件的包名

       如下是我自己研究过程的总结,想知道结果的直接看最后即可。

==============================过程总结==========================================

       在上篇博客中我写到,在远程控制音乐软件的实现中,我在4.4系统里遇到了问题。需要获取到当前播放的音乐软件的包名才能解决。就在进行了一定时间的查找,意外的发现android貌似没有提供这样的api。这一下我一下丢失了方向。甚至想到直接获取系统中所有的包名,直接搜索常用音乐软件来草草完成。

      最后由于时间比较充裕,就多研究了一段时间,突然想到,这既然是系统的为了耳机来完成远程控制的动作,那么系统应该会注意到这个问题才对。果然,我使用耳机上的按钮来操作,则没有任何问题。所以我开始研究android的系统源码究竟是如何做到这一点的。耳机控制需要使用Intent的,在intent.java(以下代码均来源于android4.4源码)中可以找到如下字段: 

       public static final String ACTION_MEDIA_BUTTON = "android.intent.action.MEDIA_BUTTON";

这就是android自己用于耳机控制播放器的intent action字段了,我们可以利用它来找到相关代码,很容易我们找到了这个类MediaFocusControl类,其中包含方法dispatchMediaKeyEvent():

              private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {

                              .................

                          Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);

                            ....................

                          mRCStack.peek().mMediaIntent.send(mContext,

                            needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,

                            keyIntent, this, mEventHandler);

                            ....................

              }

这里讲intent发送了出去,具体去了哪里我们不用操心,我们只要知道谁用了这个intent。这里的mRCStack是一个Stack类,其中包含着我们需要的RemoteControlStackEntry类对象。我们继续查看,这个对象是在哪被放入Stack中的,最后找到pushMediaButtonReceiver_syncAfRcs()方法,其中关键部分如下:     

           private boolean pushMediaButtonReceiver_syncAfRcs(PendingIntent mediaIntent,

                    ComponentName target, IBinder token) {

                     ...........

                      if (!wasInsideStack) {

                             rcse = new RemoteControlStackEntry(this, mediaIntent, target, token);

                      }

                             mRCStack.push(rcse); //rcse被push进mRCStack

                      ..........

          }

由此,可以知道这个rcse就是我们需要的对象,而他是被直接new出来的,则这个构造函数的参数就变的很重要了。查看构造函数如下:

           public RemoteControlStackEntry(MediaFocusControl controller, PendingIntent mediaIntent,

                      ComponentName eventReceiver, IBinder token)

可以发现,里面包含了一个重要的ComponentName对象,这个对象在android中就带表着一个应用程序,看来这就是我们要找的音乐播放软件了。那么他是如何被系统拿出来的呢?我们继续找。在刚刚的构造方法创建对象过程中,这个参数是使用target变量直接放入的。而这个target是pushMediaButtonReceiver_syncAfRcs方法的一个参数,那么继续根据这个线索向上找。发现这个参量是经过registerMediaButtonIntent()传入,但是仍然是方法的一个参数,那么继续找,发现找到了源头:

           protected void restoreMediaButtonReceiver() {

                         ................

                         String receiverName = Settings.System.getStringForUser(mContentResolver,

                                           Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);

                         ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);

                         ................

                         registerMediaButtonIntent(pi, eventReceiver, null);

            }

由此我们发现了ComponentName对象的创建过程,它使用了一个receiverName对象,这个是个String,他就是我们的目标了。这是用了system的一个方法来创建,进入发现他竟然是个hide的方法,无法作为api使用。后来分析了方法名才释然,这个适用于多用户的,android4.3以后准备加入用户系统,可惜还不完善所以不给我们用,这肯定是有一般方法的,很快找到了getString方法,其实他就是调用了getStringForUser而已,只是屏蔽了id的参数。但是还有一个小问题,其实Settings.System.MEDIA_BUTTON_RECEIVER字段也是个hide字段,是不可以当做api来用的,我想这就系统根本就是不想提供获取正在播放音乐的软件的方法,不过既然是个字段就没问题了,我们查看一下源码就能看到MEDIA_BUTTON_RECEIVER字段其实就代表着"media_button_receiver"而已。到这里我们已经可以实现功能了。方法如下,其实就简单的三句即可:

================================功能实现===========================================

         String receiverName = Settings.System.getString(this.getContentResolver(),

        "media_button_receiver");

         ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);

         String musicPlayerPkgName = eventReceiver.getPackageName();

我们即可得到我们所需要的包名。

猜你喜欢

转载自liguanyi11111.iteye.com/blog/2098253