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 不把接口以文檔的形式公佈於衆呢?真是“猶抱琵琶半遮面”。–或者它有公佈,我沒有看到?