Android 1.6的源码共包括了21个核心应用,分布在 package/apps下,其中 Music 应用提供了音乐播放功能,在各 GPhone 中差不多都能看到。但是这些核心应用本不属于 Framework,因此无法在 SDK Document 中看到其类和方法的说明,更无法在外部引入它们的类,Music也不例外。
工作中遇到这么一个应用场景:用户在文档阅读的 Activity 中,要能够控制音乐播放,包括:上一首、下一首、暂停和继续,以及获取正在播放的音乐的名称。由于无法引入 Music 应用的类,自然也就无法通过 API 直接实现这些功能。
查遍了 Dev Guide 和 Reference,也没有找到蛛丝马迹,于是索性直接进入到 Music 的源码探个究竟。经过初步的分析,将问题的焦点定位在 com.android.music.MediaPlaybackService 上,该类是个 Service 类,负责在后台播放音乐等工作。
该类声明并实例化了一个 BroadcastReceiver,并在 onCreate 中进行了注册,那么这个 BroadcastReceiver 到底接收哪些 Intent 呢?通过代码可以一目了然:
IntentFilter commandFilter = new IntentFilter();
commandFilter.addAction(SERVICECMD);
commandFilter.addAction(TOGGLEPAUSE_ACTION);
commandFilter.addAction(PAUSE_ACTION);
commandFilter.addAction(NEXT_ACTION);
commandFilter.addAction(PREVIOUS_ACTION);
registerReceiver(mIntentReceiver, commandFilter);
找到上述代码中的常量声明,可以看出这些就是控制音乐播放的action name了。
public static final String SERVICECMD = “com.android.music.musicservicecommand”;
public static final String CMDNAME = “command”;
public static final String CMDTOGGLEPAUSE = “togglepause”;
public static final String CMDSTOP = “stop”;
public static final String CMDPAUSE = “pause”;
public static final String CMDPREVIOUS = “previous”;
public static final String CMDNEXT = “next”;
public static final String TOGGLEPAUSE_ACTION = “com.android.music.musicservicecommand.togglepause”;
public static final String PAUSE_ACTION = “com.android.music.musicservicecommand.pause”;
public static final String PREVIOUS_ACTION = “com.android.music.musicservicecommand.previous”;
public static final String NEXT_ACTION = “com.android.music.musicservicecommand.next”;
然后再回过头来看 BroadcastReceiver是如何处理这些 Intent 的,通过其 onCreate 方法可以看出控制音乐播放有两种方式。其一是设置 action name 为 SERVICECMD,然后通过 Intent 的 extra 设置 command,其值可以是 CMDTOGGLEPAUSE、CMDSTOP、CMDPAUSE、CMDPREVIOUS 或 CMDNEXT。
其二是设置 action name 为 TOGGLEPAUSE_ACTION、PAUSE_ACTION、PREVIOUS_ACTION 或 NEXT_ACTION。
看起来第二种方式更简洁,依此思路,逆向操作也就可以解决起初的问题了,如下代码演示了如何控制下一首:
Intent intent = new Intent (”com.android.music.musicservicecommand.next”);
sendBroadcast(intent);
如果我们在此基础上更深入一步,控制音乐播放的同时,我们还需要得到正在播放的音乐名称,这又该怎么实现呢?
Android Framework 提供了非常棒的 android.media,凡音乐播放都离不开 MediaPlayer,该类定义了七个内部类,其中 MediaPlayer.OnPreparedListener 是这么描述的:
Interface definition for a callback to be invoked when the media source is ready for playback.
媒体文件准备结束,准备播放时,Android 就会调用这个这个内部类,在 MediaPlaybackService 则是 notifyChange(String what) 方法进行处理:
private void notifyChange(String what) {
Intent i = new Intent(what);
i.putExtra(”id”, Integer.valueOf(getAudioId()));
i.putExtra(”artist”, getArtistName());
i.putExtra(”album”,getAlbumName());
i.putExtra(”track”, getTrackName());
sendBroadcast(i);
// …… }
那么在我们的应用当中,则需要创建 一个BroadcastReceiver,过滤对应的 action name,也就是常量 META_CHANGED 对应的值即可达到目标。
到了这里,回顾一下解决问题的过程,不由让人有些愤愤不平,为什么 Android 不把接口以文档的形式公布于众呢?真是“犹抱琵琶半遮面”。–或者它有公布,我没有看到?