ophone平台提供了完整的多媒體解決方案。為開發者提供了統一的,簡單易用的開發接口。本文首先介紹了ophone平台的多媒體架構,然後詳細介紹了 在ophone平台上開發音樂播放程式所需的基本知識。通過一步一步建構一個簡單的音樂播放器示例程式,來幫助讀者了解具體的開發過程。該示例涵蓋了application,activity,service,intent,broadcast receiver等基本概念,使讀者對ophone程式的開發有一個全面的了解,進一步鞏固和熟悉這些基本概念。最後介紹了如何利用mat工具分析ophone
程式。
本文适合ophone平台開發的初學者閱讀。(作者:cmri 孟钊)
ophone平台的多媒體架構
在開始建構我們的示例程式前,先讓我們大概了解一下ophone平台的多媒體架構。
圖 一
圖一是ophone平台的整體架構結構,從圖上我們可以看出ophone平台大緻可以分成以下幾個層次:
最上層是application層。它包含了主屏,電話,浏覽器,位址本等核心的應用程式。我們将開發的音樂播放器也屬于這一層。
第二層是application framework層。這一層為開發者提供了完整的程式設計接口。多媒體部分提供了mediaplayer, mediarecorder等接口。同時mediaprovider,mediascanner等系統服務也對媒體檔案的管理提供了支援。本文将重點介紹 它們的使用。
第三層是library層, 它由一系列的c/c++庫組成,這些庫的能力通過jni封裝成java接口,由application framework層提供給開發者。多媒體系統庫opencore,它是ophone多媒體的核心,來源于packetvideo。它非常複雜,提供了完 整的多媒體解決方案。
最底層為linux kernel和驅動,負責與硬體的資料互動等。
圖二說明了在ophone平台中播放音樂檔案時的調用關系。
對于應用程式開發者來說,需要重點學習和關注的是如何使用appliation framework層提供給開發者的接口。
音樂媒體資訊的管理
在開始構架程式之前,我們需要準備一下必須的基本知識。首先來了解一下在ophone平台中應該如何擷取音樂檔案的資訊以及如何管理這些資訊。
ophone系統提供了mediascanner,mediaprovider,mediastore等接口,并且提供了一套資料庫表格,通過content provider的方式提供給使用者。當手機開機或者有sd卡插拔等事件發生時,系統将會自動掃描sd卡和手機記憶體上的媒體檔案,如audio,video,圖檔等,将相應的資訊放到定義好的資料庫表格中。在這個程式中,我們不需要關心如何去掃描手機中的檔案,隻要了解如何查詢和使用 這些資訊就可以了。
mediastore中定義了一系列的資料表格,通過contentresolver提供的查詢接口,我們可以得到各種需要的資訊。下面我們重點介紹如何管理sd卡上的音樂檔案資訊。
先來了解一下contentresolver的查詢接口:
view plaincopy to clipboardprint?
cursor query(uri uri, string[] projection, string selection, string[] selectionargs, string sortorder);
[java]
view plaincopyprint?
uri:指明要查詢的資料庫名稱加上表的名稱,從mediastore中我們可以找到相應資訊的參數,具體請參考開發文檔。
projection: 指定查詢資料庫表中的哪幾列,傳回的遊标中将包括相應的資訊。null則傳回所有資訊。
selection: 指定查詢條件
selectionargs:參數selection裡有 ?這個符号是,這裡可以以實際值代替這個問号。如果selection這個沒有?的話,那麼這個string數組可以為null。
sortorder:指定查詢結果的排列順序
查詢所有歌曲:
cursor cursor = query(mediastore.audio.media.external_content_uri,
null,
null,
null, mediastore.audio.media.default_sort_order);
null, null, mediastore.audio.media.default_sort_order);
該指令将傳回所有在外部存儲卡上的音樂檔案的資訊,其中常用的資訊如下:
mediastore.audio.media._id:歌曲id
int id = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.media._id));
mediastore.audio.media.title:歌曲的名稱
string tilte = cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.media.title));
mediastore.audio.media.album :歌曲的專輯名
string album = cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.media.album));
mediastore.audio.media.artist:歌曲的歌手名
string artist = cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.media.artist));
mediastore.audio.media.data:歌曲檔案的路徑
string url = cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.media.data));
mediastore.audio.media.duration:歌曲的總播放時長
int duration = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.media.duration));
mediastore.audio.media.size: 歌曲檔案的大小
int size = cursor.getlong(cursor.getcolumnindexorthrow(mediastore.audio.media.size));
查詢歌手資訊:
cursor cursor = query(mediastore.audio.artists.external_content_uri,
null, null,
mediastore.audio.artists.default_sort_order);
該指令将傳回所有在外部存儲卡上的歌手資訊,其中常用的資訊如下:
mediastore.audio.artists._id:歌手id
int id = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.artists._id));
mediastore.audio.artists.artist :歌手姓名
string name = cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.artists.artist));
mediastore.audio.artists.number_of_albums: 共有多少該歌手的專輯
int numofalbum = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.artists.number_of_albums));
mediastore.audio.artists.number_of_tracks: 共有多少該歌手的歌曲
int numofsong = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.artists.number_of_tracks));
查詢專輯資訊:
cursor cursor = query(mediastore.audio.albums.external_content_uri,
null, null,null,
mediastore.audio.albums.default_sort_order);
該指令将傳回所有在外部存儲卡上的專輯資訊,其中常用的資訊如下:
mediastore.audio.albums._id :專輯id
int id = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.albums._id));
mediastore.audio.albums.album:專輯名稱
string name = cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.albums.album));
mediastore.audio.albums.number_of_songs:共用多少歌曲屬于該專輯
int numofsong = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.albums.number_of_songs));
查詢播放清單
cursor cursor = query(mediastore.audio.playlists.external_content_uri,
mediastore.audio.playlists.date_added +
" asc");
mediastore.audio.playlists.date_added + " asc");
該指令将傳回所有在外部存儲卡上的專輯資訊,其中常用的資訊如下:
mediastore.audio.playlists._id :播放清單id
int id = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.playlists._id));
mediastore.audio.playlists.name:播放清單名稱
string name = cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.playlists.name));
mediastore.audio.playlists.date_added :添加時間
long dateadded = cursor.getlong(cursor.getcolumnindexorthrow(mediastore.audio.playlists.date_added));
mediastore.audio.playlists.date_modified :修改時間
long datemodified = cursor.getlong(cursor.getcolumnindexorthrow(mediastore.audio.playlists.date_modified));
通過組合這些查詢結果,指定查詢條件,使用者可以很友善的查詢指定的媒體資訊,比如:查詢屬于指定歌手(歌手id 為 aid)的歌曲:
query(mediastore.audio.media.external_content_uri,
mediastore.audio.media.artist_id +
"=" + aid, null,
mediastore.audio.media.title);
查詢屬于指定專輯(專輯id 為 aid)的歌曲:
return query(mediastore.audio.media.external_content_uri,
mediastore.audio.media.album_id +
以上我們重點介紹了音樂媒體資訊的查詢方法,對于媒體資訊的增删改等操作主要集中在對播放清單的管理上,也是通過content resolver的insert,update,delete等接口來實作的。隻要搞清楚了各個參數的含義,相應uri以及各個字段的義,很容易實作。由 于篇幅原因,我們不再詳細介紹,有興趣的朋友可以檢視ophone開發文檔。
音樂播放
音樂檔案的播放功能是由mediaplayer類實作的,mediaplayer提供了常用的接口,比如播放,暫停,停止,快速定位等。
播放音樂檔案的基本調用流程:
生成mediaplayer執行個體。
設定播放源(檔案)
準備播放
開始播放
mediaplayer mp = new mediaplayer();
mp.setdatasource(file_to_play);
mp.prepare();
mp.start();
以上代碼即可以完成最簡單的音樂播放功能。
除了mediaplayer類,我們還需要注意幾個播放器件listener的使用,它們提供了播放器的更多的狀态資訊。
1.mediaplayer.onbufferingupdatelistener
當播放網絡上的媒體檔案或者流媒體時 mediaplayer.onbufferingupdatelistener 的onbufferingupdate(mediaplayer mp, int percent)接口函數會被回調,通知目前的緩沖進度資訊。
通過setonbufferingupdatelistener(mediaplayer.onbufferingupdatelistener listener) 函數來注冊該listener
2.mediaplayer.oncompletionlistener
目前歌曲播放結束後,mediaplayer.oncompletionlistener的 oncompletion(mediaplayer mp) 接口會被回調,通知歌曲結束事件。
通過setoncompletionlistener(mediaplayer.oncompletionlistener listener) 函數來注冊該監聽器
3.mediaplayer.onerrorlistener
當由于某種原因,mediaplayer進入錯誤狀态時,mediaplayer.onbufferingupdatelistener的onerror(mediaplayer mp, int what, int extra)接口會被回調,通知錯誤資訊。此時mediaplayer 應該調用reset()函數,将mediaplayer重新置于idle狀态。如果發生無法回複的錯誤,需要重新擷取mediaplayer的執行個體。
4.mediaplayer.onpreparedlistener
當播放網絡媒體檔案或流媒體時,播放器的準備時間較長,播放器準備完畢可以開始播放時,mediaplayer.onpreparedlistener的onprepared(mediaplayer mp)接口會被回調,通知該資訊。
當播放器需要支援播放流媒體或者網絡媒體檔案時,建議使用prepareasync()接口調用來準備播放器,同時通過mediaplayer.onpreparedlistener來監聽prepared資訊。這樣可以避免因為網絡等因素造成的mediaplayer準 備時間過長進而導緻程式長時間無響應。
建構音樂播放器程式
在學習了媒體資訊管理和媒體播放的基本内容後,我們現在可以開始動手建構我們的簡單點傳播放器示例程式了。
一.建立工程
在eclipse開發環境中建立一個新的android project.
file > new > android project.
設定工程名為musicplayerdemo, 設定packages名為 com.ophone
二.指定程式的application,添加musicplayerdemoapp
添加musicplayerdemoapp類,它繼承自 android.app.application。
application類用來存儲程式的狀态,它存在于整個程式的生命周期之中。
修改androidmanifest.xml如下,指定musicplayerdemoapp為示例程式的application.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ophone"
android:versioncode="1"
android:versionname="1.0">
<application android:name="musicplayerdemoapp"
android:icon="@drawable/icon"
android:label="@string/app_name">
</application>
</manifest>
我們需要注意application的兩個函數: oncreate() 和 onterminate(). 當程式開始運作時,oncreate()函數會首先被調用,此時沒有任何其他的對象在運作,在這裡我們可以進行一些初始化的工作。當程式結束時, onterminate()函數會被調用,程式程序将會退出,我們可以在此做一些最終的清理工作。需要注意的是,當因為系統資源緊張等問題,程式被系統kill的時候,onterminate()不會被調用到,程式将直接退出。
稍後我們再來修改musicplayerdemoapp,先往下繼續。
三.管理音樂資訊的類musicdbcontroller
為了使接口整潔,便于管理和使用,我們将在第三章介紹的 查詢管理音樂資訊的方法統一封裝在musicdbcontroller類中。
public static musicdbcontroller getinstance(musicplayerdemoapp app) {
if(sinstance == null) {
sinstance = new musicdbcontroller(app);
}
return sinstance;
}
private musicdbcontroller(musicplayerdemoapp app) {
mapp = app;
}
private cursor query(uri _uri, string[] prjs, string selections,
string[] selectargs, string order) {
contentresolver resolver = mapp.getcontentresolver();
if (resolver ==
null) {
return null;
return resolver.query(_uri, prjs, selections, selectargs,
order);
musicdbcontroller采用單例模式,使程式中隻有唯一的執行個體。我們傳入musicplayerdemoapp 作為context生成content resolver,用來查詢媒體庫。
現在,我們修改musicplayerdemoapp,添加一個musicdbcontroller的成員,并在oncreate()中初始化它。
private musicdbcontroller mdbcontorller =
null;
public void oncreate() {
// todo auto-generated method stub
super.oncreate();
// init musicdbcontroller
mdbcontorller = musicdbcontroller.getinstance(this);
并且提供一個擷取musicdbcontroller的接口:
public musicdbcontroller getmusicdbcontroller(){
return mdbcontorller;
這樣程式中的任何activity和serivce都可以通過getapplicatio()函數得到musicplayerdemoapp, 再通過getmusicdbcontroller()接口擷取musicdbcontroller,進而擷取所需要的媒體資訊。
四.展示媒體庫-musiclistactivity 和 musiclistadapter。
首先添加musiclistadapter,它繼承自simplecursoradapter。通過重載bindview()函數, 把媒體庫資訊綁定到指定的listview上。
我們使用android.r.layout.cmcc_list_5作為listview的layout,它的布局定義如下:
android.r.layout.cmcc_list_5:
android.r.id.listicon1 圖檔
android.r.id.text1 左上文字
android.r.id.text2 左下文字
android.r.id.text3 右下文字
public void bindview(view view, context context, cursor cursor) {
super.bindview(view, context, cursor);
textview titleview = (textview) view.findviewbyid(android.r.id.text1);
textview artistview = (textview) view.findviewbyid(android.r.id.text2);
textview durationview = (textview) view.findviewbyid(android.r.id.text3);
imageview imageview = (imageview) view.findviewbyid(android.r.id.listicon1);
// set icon
imageview.setimageresource(r.drawable.cmcc_list_music);
// set track name
titleview.settext(cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.media.title)));
// set artist name
artistview.settext(cursor.getstring(cursor.getcolumnindexorthrow(mediastore.audio.media.artist)));
// set duration
int duration = cursor.getint(cursor.getcolumnindexorthrow(mediastore.audio.media.duration));
durationview.settext(maketimestring(duration));
注意,上面這段代碼中的android.r.id.text1,android.r.id.text2,android.r.id.text3 和 android.r.id.listicon1是在我們傳入中的listview(android.r.layout.cmcc_list_5)的layout中定義的。如果你使用了自己定義的layout,請把它們替換成你自己定義的widget id。
現在可以來添加我們的第一個activity -musiclistactivity,它以list的形式展示了所有歌曲。musiclistactivity繼承自listactivity。
在oncreate()中擷取musicdbcontroller的執行個體,為擷取歌曲資訊做準備。
private musicdbcontroller mdbcontroller =
null;
/** called when the activity is first created. */
@override
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.main);
mdbcontroller = ((musicplayerdemoapp)getapplication()).getmusicdbcontroller();
通過musiclistadapter,我們将從musicdbcontroller中拿到的媒體庫資訊,綁定到listview,我們在onresume()完成這個工作。
protected void onresume() {
super.onresume();
mcursor = mdbcontroller.getallsongs();
musiclistadapter adapter = new musiclistadapter(this, android.r.layout.cmcc_list_5, mcursor,
new string[]{}, new
int[]{});
setlistadapter(adapter);
}
将musiclistactivity添加到androidmanifest.xml中
<activity android:name=".musiclistactivity">
<intent-filter>
<action android:name="android.intent.action.main" />
<category android:name="android.intent.category.launcher" />
</intent-filter>
</activity>
現在運作一下我們的程式,它已經可以展現給你媒體庫的音樂清單了。
同樣的,仿照上面的過程,我們還可以添加展示專輯清單,藝術家清單等等activity,我們就不再一一介紹了。
五.背景播放-使用service
現在我們需要考慮如何來播放這些媒體庫中的檔案了。我們希望當使用者退出這個程式界面後,我們的程式仍然能夠繼續播放歌曲,比如使用者在讀郵件時,可以聽聽音 樂。為了達到背景播放的效果,需要使用service。當程式的所有activity都退出後,service仍然可以在背景運作。在這個示例中我們使用local service,它與應用程式運作在同一個程序中。(我們甚至可以不使用bind service就直接獲得它的句柄,調用它所提供的函數。)
首先,建立一個musicplaybackservice類,它繼承自android.app.service,重載onbind方法,傳回自 定義的localbinder,通過localbinder的getservice()方法就可以獲得musicplaybackservice的句柄 了。
private final ibinder mbinder =
new localbinder();
public class localbinder
extends binder {
public musicplaybackservice getservice() {
return musicplaybackservice.this;
public ibinder onbind(intent intent) {
return mbinder;
我們繼續完成musicplaybackservice的基本構架,添加一個mediaplayer成員,并在oncreate()函數中對其進行初始化,它将負責音樂播放的主要功能。
private mediaplayer mmediaplayer =
mmediaplayer = new mediaplayer();
構架完成musicplaybackservice的基本架構後,我們要定義一些常用的控制接口了,其他子產品通過這些接口,可以控制音樂的播放,暫停,停止等功能。
public void setdatasource(string path) {
try {
mmediaplayer.reset();
mmediaplayer.setdatasource(path);
mmediaplayer.prepare();
} catch (ioexception e) {
return;
} catch (illegalargumentexception e) {
public void start() {
mmediaplayer.start();
public void stop() {
mmediaplayer.stop();
public void pause() {
mmediaplayer.pause();
public boolean isplaying() {
return mmediaplayer.isplaying();
public int getduration() {
return mmediaplayer.getduration();
public int getposition() {
return mmediaplayer.getcurrentposition();
public long seek(long whereto) {
mmediaplayer.seekto((int) whereto);
return whereto;
最後,修改androidmanifest.xml,添加musicplaybackservice的定義。
<service android:name=".musicplaybackservice" android:exported="true" >
<action android:name="com.ophone.musicplaybackservice" />
</intent-filter>
</service>
六.開始播放歌曲
musicplaybackservice準備就緒,我們可以利用它來播放歌曲了。修改musiclistactivity,在 oncreate() 中通過startservice()函數啟動musicplaybackservice,并通過bindservice()函數與之綁定。當綁定完成 時,serviceconnection的 onserviceconnected()接口将被調用。
private musicplaybackservice mplaybackservice =
private serviceconnection mplaybackconnection =
new serviceconnection() {
public void onserviceconnected(componentname classname, ibinder service) {
mplaybackservice = ((musicplaybackservice.localbinder)service).getservice();
public void onservicedisconnected(componentname classname) {
mplaybackservice = null;
};
public void oncreate(bundle savedinstancestate) {
setcontentview(r.layout.list_layout);
// bind playback service
startservice(new intent(this,musicplaybackservice.class));
bindservice(new intent(this,musicplaybackservice.class), mplaybackconnection, context.bind_auto_create);
為musiclistactivity添加點選事件處理,當使用者點選一個音樂item時,會開始自動播放該歌曲,當使用者點選一個item時,onlistitemclick()函數會被調用。
protected void onlistitemclick(listview l, view v,
int position, long id) {
super.onlistitemclick(l, v, position, id);
if (mcursor ==
null ||mcursor.getcount() == 0) {
mcursor.movetoposition(position);
string url = mcursor
.getstring(mcursor
.getcolumnindexorthrow(mediastore.audio.media.data));
mplaybackservice.setdatasource(url);
mplaybackservice.start();
現在趕緊運作一下程式吧,看看是不是已經可以播放音樂了呢。
七. 播放控制-使用intent和broadcast receiver
目前我們隻能播放音樂,還無法控制音樂的播放,暫停,停止,等等,讓我們進一步來完善這個播放程式,給它添加兩個控制按鈕。
修改musiclistactivity的layout檔案list_layout.xml如下:
<linearlayout
android:id="@+id/widget1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
>
<relativelayout android:id="@+id/control_panel"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
>
<textview android:id="@+id/show_text"
android:textsize="20sp"
android:text="@string/click_to_play"/>
<button android:id="@+id/play_pause_btn"
android:layout_width="100px"
android:layout_height="wrap_content"
android:layout_alignparentleft="true"
android:visibility="invisible"
android:text="@string/play"/>
<button android:id="@+id/stop_btn"
android:layout_alignparentright="true"
android:text="@string/stop"/>
</relativelayout>
<listview android:id="@id/android:list"
android:layout_height="fill_parent"
android:cachecolorhint="#00000000"/>
<textview android:id="@id/android:empty"
android:text="@string/no_music"/>
</linearlayout>
在musiclistactivity中,添加兩個按鈕點選事件的處理程式,通過button的setonclicklistener()函數,為button添加一個button.onclicklistener,當有點選事件發生時,button.onclicklistener的onclick()接口将被調用。
private textview mtextview =
private button mplaypausebutton =
private button mstopbutton =
在oncreate函數中,增加如下的代碼:
mtextview = (textview)findviewbyid(r.id.show_text);
mplaypausebutton = (button) findviewbyid(r.id.play_pause_btn);
mstopbutton = (button) findviewbyid(r.id.stop_btn);
mplaypausebutton.setonclicklistener(new button.onclicklistener() {
public void onclick(view v) {
// perform action on click
if (mplaybackservice !=
null && mplaybackservice.isplaying()) {
mplaybackservice.pause();
mplaypausebutton.settext(r.string.play);
} else
if (mplaybackservice != null){
mplaybackservice.start();
mplaypausebutton.settext(r.string.pause);
}
}
});
mstopbutton.setonclicklistener(new button.onclicklistener() {
public
void onclick(view v) {
null ) {
mtextview.setvisibility(view.visible);
mplaypausebutton.setvisibility(view.invisible);
mstopbutton.setvisibility(view.invisible);
mplaybackservice.stop();
現在運作程式,我們還看不到這兩個控制按鈕,預設狀态下他們是不可見狀态。程式剛啟動時,預設顯示提示資訊。當播放器狀态發生改變,有歌曲進行播放時,我 們顯示控制按鈕,隐藏提示資訊。我們使用intent和broadcast receiver來實作這個功能。
定義準備完畢和播放完畢的action string
public static
final string player_prepare_end =
"com.ophone.musicplaybackservice.prepared";
public static
final string play_completed = "com.ophone.musicplaybackservice.playcompleted";
播放器狀态發生改變的時候,通過intent的形式,将消息廣播出去,給mediaplayer添加mediaplayer.onpreparedlistener和mediaplayer.oncompletionlistener,監聽準備完畢和播 放結束的消息。
mediaplayer.oncompletionlistener mcompletelistener =
new mediaplayer.oncompletionlistener() {
public void oncompletion(mediaplayer mp) {
broadcastevent(play_completed);
};
mediaplayer.onpreparedlistener mpreparelistener =
new mediaplayer.onpreparedlistener() {
public void onprepared(mediaplayer mp) {
broadcastevent(player_prepare_end);
private void broadcastevent(string what) {
intent i = new intent(what);
sendbroadcast(i);
修改musicplaybackservice,在mediaplayer中注冊這個兩個listener:
public void oncreate() {
mmediaplayer = new mediaplayer();
mmediaplayer.setonpreparedlistener(mpreparelistener);
mmediaplayer.setoncompletionlistener(mcompletelistener);
在musiclistactivity中,我們定義一個broadcastreceiver來處理這兩個消息:
protected broadcastreceiver mplayerevtreceiver =
new broadcastreceiver() {
@override
public void onreceive(context context, intent intent) {
string action = intent.getaction();
if (action.equals(musicplaybackservice.player_prepare_end)) {
// will begin to play
mtextview.setvisibility(view.invisible);
mplaypausebutton.setvisibility(view.visible);
mstopbutton.setvisibility(view.visible);
mplaypausebutton.settext(r.string.pause);
} else if(action.equals(musicplaybackservice.play_completed)) {
mplaypausebutton.settext(r.string.play);
在oncreate()函數中,注冊這個broadcastreceiver來監聽player_prepare_end 和play_completed 這兩個資訊 ,在oncreate函數中添加下面的代碼:
intentfilter filter = new intentfilter();
filter.addaction(musicplaybackservice.player_prepare_end);
filter.addaction(musicplaybackservice.play_completed);
registerreceiver(mplayerevtreceiver, filter);
ok,現在我們的音樂播放器已經成型了,馬上運作一下吧。
給程式加點新功能
下面介紹的功能,在我們的示例代碼中并沒有實作,如果您感興趣的話,可以按照下文介紹的大概步驟,添加到程式中,他們其實都很簡單。
1.利用alarm service實作簡單的鬧鈴功能。
alarm service是ophone平台提供的一個系統服務。程式可以向alarm service注冊一個pendingintent,當到達注冊時間的時候,alarm service會發出這個事先注冊的intent,程式監聽這個intent就可以達到定時的效果。
1)添加一個broadcastreceiver
public class startalarm
extends broadcastreceiver {
// 添加處理程式,啟動播放。
在androidmanifest.xml中添加定義:
<receiver android:name=".startalarm" />
2)注冊alarm service
intent startintent = new intent(context, startalarm.class);
pendingintent startsender = pendingintent.getbroadcast(
context, 0, startintent,
0);
// schedule the alarm! starttimemillis 是定時時間
alarmmanager am = (alarmmanager)getsystemservice(alarm_service);
am.setrepeating(alarmmanager.rtc_wakeup, starttimemillis,
24 *
60 * 60 *
1000, startsender);
ok,我們就完成了定時注冊,當注冊時間到達時,即使程式沒有運作,也會被喚醒,startalarm的onreceive()函數被調用,開始播放音樂。一個簡單的鬧鐘功能就實作了。感興趣的朋友可以馬上動手試驗試驗。
2.設定振鈴
當我們發現了一首非常好聽的歌曲,想把它設定成來電振鈴, 如何實作呢?很簡單,隻需要如下兩個步驟。
第一步,更新歌曲在media provider資料庫中的資訊,
将 mediastore.audio.media.is_ringtone,
mediastore.audio.media.is_alarm,
mediastore.audio.media.is_notification都置成 1。
假設歌曲的id為 songid:
contentresolver resolver = ctx.getcontentresolver();
// set the flag in the database to mark this as a ringtone
uri ringuri = contenturis.withappendedid(mediastore.audio.media.external_content_uri, songid);
try {
contentvalues values = new contentvalues(2);
values.put(mediastore.audio.media.is_ringtone,
"1");
values.put(mediastore.audio.media.is_alarm,
values.put(mediastore.audio.media.is_notification,
"1");
resolver.update(ringuri, values, null,
null);
} catch (unsupportedoperationexception ex) {
第二步,通過android.provider.settings.profile的setringtone接口,設定歌曲為振鈴:
settings.profile.setringtone(resolver, ringuri);
現在給自己打個電話試試看,是不是振鈴已經起作用了
使用mat分析ophone程式
我們的示例代碼已經完成了,大家可以按照上文的步驟自己一步一步來構造自己的音樂播放器,也可以使用附錄的源代碼包,将工程導入進eclipse直接體驗一下。最後和大家分享一下使用mat分析ophone程式的方法。
通常來說我們調試ophone程式有兩個最常見的方法,一,利用ophone平台提供的android.util.log通過log資訊來分析 錯誤發生的原因。 二,通過設定斷點,一步一步的跟蹤程式發現問題。這兩個方法非常有效,介紹相關方法的文章也很多,大家google一下就找到了。
還有一類常見的問題就是memory leak。對記憶體洩漏這類問題,以上兩種方法不是很有效,在ddms工具裡面,我們也基本上隻能檢視到heap的使用情況,對分析問題幫助不大。我們可以 利用eclipse mat (memory analyzer tool)工具來分析此類問題。eclipse memory analyzer是一個快速并且功能強大的java heap分析器,能夠幫助你查找記憶體洩漏和減少記憶體消耗。
如何安裝使用mat工具,請到http://www.eclipse.org/mat/學習,我們主要來介紹一下如何在ophone上得到程式運作的heap dump資訊。
adb shell 登陸到手機或模拟器
su – 切換到root權限
chmod 777 /data/misc, 使/data/misc目錄具有讀寫權限
通過ps指令,找到要調試的程式的pid
kill -10 pid
在/data/misc 目錄下,會生成檔案名類似heap-dump-xxxxx-pidxxx.hprof的檔案。
通過adb pull 指令将.hprof檔案拽到pc端
使用ophone sdk提供的hprof-conv工具将ophone生成的hprof檔案轉換成mat識别的标準格式。例如:
hprof-conv heap-dump-xxxxx-pidxxx.hprof standard-dump-file.hprof
9. 使用mat工具打開 standard-dump-file.hprof, 你将看到類似下圖的分析報告。
分析報告提供了詳盡的heap資訊,同時還指出了可疑的記憶體洩漏的對象。
大家可以根據mat提供的詳細heap資訊,查找漏洞了。
附錄:源代碼:
uploads/file/musicplayerdemo.zip