閱讀999 返回首頁    go 阿裏雲 go 技術社區[雲棲]


OPhone程序開發入門之音樂播放器

OPhone平台提供了完整的多媒體解決方案。為開發者提供了統一的,簡單易用的開發接口。本文首先介紹了OPhone平台的多媒體框架,然後詳細介紹了 在OPhone平台上開發音樂播放程序所需的基本知識。通過一步一步構建一個簡單的音樂播放器示例程序,來幫助讀者了解具體的開發過程。該示例涵蓋了Application,Activity,Service,Intent,BroadCast  Receiver等基本概念,使讀者對OPhone程序的開發有一個全麵的了解,進一步鞏固和熟悉這些基本概念。最後介紹了如何利用MAT工具分析OPhone 程序。

        本文適合OPhone平台開發的初學者閱讀。(作者:CMRI 孟釗)

OPhone平台的多媒體架構

        在開始構建我們的示例程序前,先讓我們大概了解一下OPhone平台的多媒體框架。

圖   一

       圖一是OPhone平台的整體框架結構,從圖上我們可以看出OPhone平台大致可以分成以下幾個層次:

  1. 最上層是Application層。它包含了主屏,電話,瀏覽器,地址本等核心的應用程序。我們將開發的音樂播放器也屬於這一層。
  2. 第二層是Application  Framework層。這一層為開發者提供了完整的編程接口。多媒體部分提供了MediaPlayer,  MediaRecorder等接口。同時MediaProvider,MediaScanner等係統服務也對媒體文件的管理提供了支持。本文將重點介紹 它們的使用。
  3. 第三層是Library層,  它由一係列的c/c++庫組成,這些庫的能力通過JNI封裝成java接口,由Application  Framework層提供給開發者。多媒體係統庫OpenCore,它是OPhone多媒體的核心,來源於PacketVideo。它非常複雜,提供了完 整的多媒體解決方案。
  4. 最底層為Linux Kernel和驅動,負責與硬件的數據交互等。

  圖二說明了在OPhone平台中播放音樂文件時的調用關係。

  對於應用程序開發者來說,需要重點學習和關注的是如何使用Appliation Framework層提供給開發者的接口。

音樂媒體信息的管理

  在開始構架程序之前,我們需要準備一下必須的基本知識。首先來了解一下在OPhone平台中應該如何獲取音樂文件的信息以及如何管理這些信息。

  OPhone係統提供了MediaScanner,MediaProvider,MediaStore等接口,並且提供了一套數據庫表格,通過Content  Provider的方式提供給用戶。當手機開機或者有SD卡插拔等事件發生時,係統將會自動掃描SD卡和手機內存上的媒體文件,如audio,video,圖片等,將相應的信息放到定義好的數據庫表格中。在這個程序中,我們不需要關心如何去掃描手機中的文件,隻要了解如何查詢和使用 這些信息就可以了。

  MediaStore中定義了一係列的數據表格,通過ContentResolver提供的查詢接口,我們可以得到各種需要的信息。下麵我們重點介紹如何管理SD卡上的音樂文件信息。

  先來了解一下ContentResolver的查詢接口:

  1. Cursor  query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); 
  1. Cursor  query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); 
Cursor  query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder);

        Uri:指明要查詢的數據庫名稱加上表的名稱,從MediaStore中我們可以找到相應信息的參數,具體請參考開發文檔。
        Projection: 指定查詢數據庫表中的哪幾列,返回的遊標中將包括相應的信息。Null則返回所有信息。
        selection: 指定查詢條件
        selectionArgs:參數selection裏有 ?這個符號是,這裏可以以實際值代替這個問號。如果selection這個沒有?的話,那麼這個String數組可以為null。
        SortOrder:指定查詢結果的排列順序

  查詢所有歌曲:

  1. Cursor cursor = query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null
  2.                 null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); 
  1. Cursor cursor = query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null
  2.                 null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER); 
Cursor cursor = query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
				null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);

  該命令將返回所有在外部存儲卡上的音樂文件的信息,其中常用的信息如下:

  1. MediaStore.Audio.Media._ID:歌曲ID 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)); 
  3.  
  4. MediaStore.Audio.Media.TITLE:歌曲的名稱 
  5. String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); 
  6.  
  7. MediaStore.Audio.Media.ALBUM :歌曲的專輯名 
  8. String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)); 
  9.  
  10.  
  11. MediaStore.Audio.Media.ARTIST:歌曲的歌手名 
  12. String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); 
  13.  
  14. MediaStore.Audio.Media.DATA:歌曲文件的路徑 
  15. String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)); 
  16.  
  17. MediaStore.Audio.Media.DURATION:歌曲的總播放時長 
  18. Int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)); 
  19.  
  20. MediaStore.Audio.Media.SIZE: 歌曲文件的大小 
  21. Int size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)); 
  1. MediaStore.Audio.Media._ID:歌曲ID 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)); 
  3.  
  4. MediaStore.Audio.Media.TITLE:歌曲的名稱 
  5. String tilte = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE)); 
  6.  
  7. MediaStore.Audio.Media.ALBUM :歌曲的專輯名 
  8. String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM)); 
  9.  
  10.  
  11. MediaStore.Audio.Media.ARTIST:歌曲的歌手名 
  12. String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)); 
  13.  
  14. MediaStore.Audio.Media.DATA:歌曲文件的路徑 
  15. String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA)); 
  16.  
  17. MediaStore.Audio.Media.DURATION:歌曲的總播放時長 
  18. Int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)); 
  19.  
  20. MediaStore.Audio.Media.SIZE: 歌曲文件的大小 
  21. Int size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE)); 
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));


        查詢歌手信息:

  1. Cursor cursor = query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, null, null, null
  2. MediaStore.Audio.Artists.DEFAULT_SORT_ORDER); 
  1. Cursor cursor = query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, null, null, null
  2. MediaStore.Audio.Artists.DEFAULT_SORT_ORDER); 
Cursor cursor = query(MediaStore.Audio.Artists.EXTERNAL_CONTENT_URI, null, null, null,
 MediaStore.Audio.Artists.DEFAULT_SORT_ORDER);

  該命令將返回所有在外部存儲卡上的歌手信息,其中常用的信息如下:

  1. MediaStore.Audio.Artists._ID:歌手id 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID)); 
  3.  
  4. MediaStore.Audio.Artists.ARTIST :歌手姓名 
  5. String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST)); 
  6.  
  7. MediaStore.Audio.Artists.NUMBER_OF_ALBUMS: 共有多少該歌手的專輯 
  8. Int numOfAlbum = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS)); 
  9.  
  10. MediaStore.Audio.Artists.NUMBER_OF_TRACKS: 共有多少該歌手的歌曲 
  11. Int numOfSong = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS)); 
  1. MediaStore.Audio.Artists._ID:歌手id 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists._ID)); 
  3.  
  4. MediaStore.Audio.Artists.ARTIST :歌手姓名 
  5. String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.ARTIST)); 
  6.  
  7. MediaStore.Audio.Artists.NUMBER_OF_ALBUMS: 共有多少該歌手的專輯 
  8. Int numOfAlbum = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_ALBUMS)); 
  9.  
  10. MediaStore.Audio.Artists.NUMBER_OF_TRACKS: 共有多少該歌手的歌曲 
  11. Int numOfSong = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Artists.NUMBER_OF_TRACKS)); 
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));

  查詢專輯信息:

  1. Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, null, null,null
  2. MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); 
  1. Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, null, null,null
  2. MediaStore.Audio.Albums.DEFAULT_SORT_ORDER); 
Cursor cursor = query(MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI, null, null,null,
 MediaStore.Audio.Albums.DEFAULT_SORT_ORDER);

    該命令將返回所有在外部存儲卡上的專輯信息,其中常用的信息如下:

  1. MediaStore.Audio.Albums._ID :專輯id 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums._ID)); 
  3.  
  4. MediaStore.Audio.Albums.ALBUM:專輯名稱 
  5. String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM)); 
  6.  
  7. MediaStore.Audio.Albums.NUMBER_OF_SONGS:共用多少歌曲屬於該專輯 
  8. Int numOfSong = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS)); 
  1. MediaStore.Audio.Albums._ID :專輯id 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums._ID)); 
  3.  
  4. MediaStore.Audio.Albums.ALBUM:專輯名稱 
  5. String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.ALBUM)); 
  6.  
  7. MediaStore.Audio.Albums.NUMBER_OF_SONGS:共用多少歌曲屬於該專輯 
  8. Int numOfSong = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Albums.NUMBER_OF_SONGS)); 
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));

  查詢播放列表

  1. Cursor cursor = query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, null, null, null
  2. MediaStore.Audio.Playlists.DATE_ADDED + " asc"); 
  1. Cursor cursor = query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, null, null, null
  2. MediaStore.Audio.Playlists.DATE_ADDED + " asc"); 
Cursor cursor = query(MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI, null, null, null,
 MediaStore.Audio.Playlists.DATE_ADDED + " asc");

      該命令將返回所有在外部存儲卡上的專輯信息,其中常用的信息如下:

  1. MediaStore.Audio.Playlists._ID :播放列表id 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID)); 
  3.  
  4. MediaStore.Audio.Playlists.NAME:播放列表名稱 
  5. String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME)); 
  6.  
  7. MediaStore.Audio.Playlists.DATE_ADDED :添加時間 
  8. long dateAdded = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_ADDED)); 
  9.  
  10. MediaStore.Audio.Playlists.DATE_MODIFIED :修改時間 
  11. long dateModified = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_MODIFIED)); 
  1. MediaStore.Audio.Playlists._ID :播放列表id 
  2. Int id = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists._ID)); 
  3.  
  4. MediaStore.Audio.Playlists.NAME:播放列表名稱 
  5. String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.NAME)); 
  6.  
  7. MediaStore.Audio.Playlists.DATE_ADDED :添加時間 
  8. long dateAdded = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_ADDED)); 
  9.  
  10. MediaStore.Audio.Playlists.DATE_MODIFIED :修改時間 
  11. long dateModified = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.DATE_MODIFIED)); 
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)的歌曲:

  1. query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null
  2.                 MediaStore.Audio.Media.ARTIST_ID + "=" + aid, null
  3.                 MediaStore.Audio.Media.TITLE); 
  1. query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null
  2.                 MediaStore.Audio.Media.ARTIST_ID + "=" + aid, null
  3.                 MediaStore.Audio.Media.TITLE); 
query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
				MediaStore.Audio.Media.ARTIST_ID + "=" + aid, null,
				MediaStore.Audio.Media.TITLE);


      查詢屬於指定專輯(專輯id 為 aid)的歌曲:

  1. return query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null
  2.                 MediaStore.Audio.Media.ALBUM_ID + "=" + aid, null
  3.                 MediaStore.Audio.Media.TITLE); 
  1. return query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null
  2.                 MediaStore.Audio.Media.ALBUM_ID + "=" + aid, null
  3.                 MediaStore.Audio.Media.TITLE); 
return query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null,
				MediaStore.Audio.Media.ALBUM_ID + "=" + aid, null,
				MediaStore.Audio.Media.TITLE);


      以上我們重點介紹了音樂媒體信息的查詢方法,對於媒體信息的增刪改等操作主要集中在對播放列表的管理上,也是通過Content  Resolver的insert,update,delete等接口來實現的。隻要搞清楚了各個參數的含義,相應URI以及各個字段的義,很容易實現。由 於篇幅原因,我們不再詳細介紹,有興趣的朋友可以查看OPhone開發文檔。

音樂播放

  音樂文件的播放功能是由MediaPlayer類實現的,MediaPlayer提供了常用的接口,比如播放,暫停,停止,快速定位等。

       播放音樂文件的基本調用流程:

  1. 生成MediaPlayer實例。
  2. 設置播放源(文件)
  3. 準備播放
  4. 開始播放    
  1. MediaPlayer mp = new MediaPlayer();   
  2. mp.setDataSource(file_to_play);   
  3. mp.prepare();   
  4. mp.start();   
  1. MediaPlayer mp = new MediaPlayer();   
  2. mp.setDataSource(file_to_play);   
  3. mp.prepare();   
  4. mp.start();   
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.

  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <manifest xmlns:android="https://schemas.android.com/apk/res/android" 
  3.       package="com.ophone" 
  4.       android:versionCode="1" 
  5.       android:versionName="1.0"
  6.     <application android:name="MusicPlayerDemoApp" 
  7.      android:icon="@drawable/icon"  
  8.      android:label="@string/app_name"
  9.     </application> 
  10. </manifest> 
  1. <?xml version="1.0" encoding="utf-8"?> 
  2. <manifest xmlns:android="https://schemas.android.com/apk/res/android" 
  3.       package="com.ophone" 
  4.       android:versionCode="1" 
  5.       android:versionName="1.0"
  6.     <application android:name="MusicPlayerDemoApp" 
  7.      android:icon="@drawable/icon"  
  8.      android:label="@string/app_name"
  9.     </application> 
  10. </manifest> 
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:andro
      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類中。

  1. public static MusicDBController getInstance(MusicPlayerDemoApp app) { 
  2.         if(sInstance == null) { 
  3.             sInstance = new MusicDBController(app); 
  4.         } 
  5.         return sInstance; 
  6.     } 
  7.      
  8.     private MusicDBController(MusicPlayerDemoApp app) { 
  9.         mApp = app; 
  10.   } 
  11.  
  12. private Cursor query(Uri _uri, String[] prjs, String selections, 
  13.             String[] selectArgs, String order) { 
  14.         ContentResolver resolver = mApp.getContentResolver(); 
  15.         if (resolver == null) { 
  16.             return null
  17.         } 
  18.          
  19.         return resolver.query(_uri, prjs, selections, selectArgs, 
  20.                 order); 
  1. public static MusicDBController getInstance(MusicPlayerDemoApp app) { 
  2.         if(sInstance == null) { 
  3.             sInstance = new MusicDBController(app); 
  4.         } 
  5.         return sInstance; 
  6.     } 
  7.      
  8.     private MusicDBController(MusicPlayerDemoApp app) { 
  9.         mApp = app; 
  10.   } 
  11.  
  12. private Cursor query(Uri _uri, String[] prjs, String selections, 
  13.             String[] selectArgs, String order) { 
  14.         ContentResolver resolver = mApp.getContentResolver(); 
  15.         if (resolver == null) { 
  16.             return null
  17.         } 
  18.          
  19.         return resolver.query(_uri, prjs, selections, selectArgs, 
  20.                 order); 
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()中初始化它。

  1. private MusicDBController mDBContorller = null;     
  2.     public void onCreate() { 
  3.         // TODO Auto-generated method stub 
  4.         super.onCreate(); 
  5.          
  6.         // init MusicDBController 
  7.         mDBContorller = MusicDBController.getInstance(this); 
  8.   } 
  9.     並且提供一個獲取MusicDBController的接口: 
  10.      
  11. public MusicDBController getMusicDBController(){ 
  12.         return mDBContorller; 
  1. private MusicDBController mDBContorller = null;     
  2.     public void onCreate() { 
  3.         // TODO Auto-generated method stub 
  4.         super.onCreate(); 
  5.          
  6.         // init MusicDBController 
  7.         mDBContorller = MusicDBController.getInstance(this); 
  8.   } 
  9.     並且提供一個獲取MusicDBController的接口: 
  10.      
  11. public MusicDBController getMusicDBController(){ 
  12.         return mDBContorller; 
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 右下文字

  1. public void bindView(View view, Context context, Cursor cursor) {    
  2.          
  3.     super.bindView(view, context, cursor); 
  4.          
  5.     TextView titleView = (TextView) view.findViewById(android.R.id.text1); 
  6.         TextView artistView = (TextView) view.findViewById(android.R.id.text2); 
  7.         TextView durationView = (TextView) view.findViewById(android.R.id.text3); 
  8.         ImageView imageView = (ImageView) view.findViewById(android.R.id.listicon1); 
  9.          
  10.         // Set icon 
  11.         imageView.setImageResource(R.drawable.cmcc_list_music); 
  12.          
  13.         // Set track name 
  14.         titleView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE))); 
  15.          
  16.         // Set artist name 
  17.         artistView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST))); 
  18.          
  19.         // Set duration 
  20.         int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)); 
  21.         durationView.setText(makeTimeString(duration)); 
  22.          
  23.     } 
  1. public void bindView(View view, Context context, Cursor cursor) {    
  2.          
  3.     super.bindView(view, context, cursor); 
  4.          
  5.     TextView titleView = (TextView) view.findViewById(android.R.id.text1); 
  6.         TextView artistView = (TextView) view.findViewById(android.R.id.text2); 
  7.         TextView durationView = (TextView) view.findViewById(android.R.id.text3); 
  8.         ImageView imageView = (ImageView) view.findViewById(android.R.id.listicon1); 
  9.          
  10.         // Set icon 
  11.         imageView.setImageResource(R.drawable.cmcc_list_music); 
  12.          
  13.         // Set track name 
  14.         titleView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE))); 
  15.          
  16.         // Set artist name 
  17.         artistView.setText(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST))); 
  18.          
  19.         // Set duration 
  20.         int duration = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION)); 
  21.         durationView.setText(makeTimeString(duration)); 
  22.          
  23.     } 
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的實例,為獲取歌曲信息做準備。

  1. private MusicDBController mDBController = null
  2.      
  3.     /** Called when the activity is first created. */ 
  4.     @Override 
  5.     public void onCreate(Bundle savedInstanceState) { 
  6.         super.onCreate(savedInstanceState); 
  7.         setContentView(R.layout.main); 
  8.          
  9.         mDBController = ((MusicPlayerDemoApp)getApplication()).getMusicDBController(); 
  10.     } 
  11. 通過MusicListAdapter,我們將從MusicDBController中拿到的媒體庫信息,綁定到ListView,我們在onResume()完成這個工作。 
  12. protected void onResume() { 
  13.         super.onResume(); 
  14.         mCursor = mDBController.getAllSongs(); 
  15.         MusicListAdapter adapter = new MusicListAdapter(this, android.R.layout.cmcc_list_5, mCursor, new String[]{}, new int[]{}); 
  16.         setListAdapter(adapter); 
  17. 將MusicListActivity添加到AndroidManifest.xml中 
  18.  
  19. <activity android:name=".MusicListActivity"
  20.             <intent-filter> 
  21.                 <action android:name="android.intent.action.MAIN" /> 
  22.                 <category android:name="android.intent.category.LAUNCHER" /> 
  23.             </intent-filter> 
  24.         </activity> 
  1. private MusicDBController mDBController = null
  2.      
  3.     /** Called when the activity is first created. */ 
  4.     @Override 
  5.     public void onCreate(Bundle savedInstanceState) { 
  6.         super.onCreate(savedInstanceState); 
  7.         setContentView(R.layout.main); 
  8.          
  9.         mDBController = ((MusicPlayerDemoApp)getApplication()).getMusicDBController(); 
  10.     } 
  11. 通過MusicListAdapter,我們將從MusicDBController中拿到的媒體庫信息,綁定到ListView,我們在onResume()完成這個工作。 
  12. protected void onResume() { 
  13.         super.onResume(); 
  14.         mCursor = mDBController.getAllSongs(); 
  15.         MusicListAdapter adapter = new MusicListAdapter(this, android.R.layout.cmcc_list_5, mCursor, new String[]{}, new int[]{}); 
  16.         setListAdapter(adapter); 
  17. 將MusicListActivity添加到AndroidManifest.xml中 
  18.  
  19. <activity android:name=".MusicListActivity"
  20.             <intent-filter> 
  21.                 <action android:name="android.intent.action.MAIN" /> 
  22.                 <category android:name="android.intent.category.LAUNCHER" /> 
  23.             </intent-filter> 
  24.         </activity> 
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的句柄 了。

  1. private final IBinder mBinder = new LocalBinder(); 
  2.      
  3.     public class LocalBinder extends Binder { 
  4.         public MusicPlaybackService getService() { 
  5.             return MusicPlaybackService.this
  6.         } 
  7.     } 
  8.      
  9.     @Override 
  10.     public IBinder onBind(Intent intent) { 
  11.         // TODO Auto-generated method stub 
  12.         return mBinder; 
  1. private final IBinder mBinder = new LocalBinder(); 
  2.      
  3.     public class LocalBinder extends Binder { 
  4.         public MusicPlaybackService getService() { 
  5.             return MusicPlaybackService.this
  6.         } 
  7.     } 
  8.      
  9.     @Override 
  10.     public IBinder onBind(Intent intent) { 
  11.         // TODO Auto-generated method stub 
  12.         return mBinder; 
private final IBinder mBinder = new LocalBinder();
    
    public class LocalBinder extends Binder {
        public MusicPlaybackService getService() {
            return MusicPlaybackService.this;
        }
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return mBinder;
}

  我們繼續完成MusicPlaybackService的基本構架,添加一個MediaPlayer成員,並在onCreate()函數中對其進行初始化,它將負責音樂播放的主要功能。

  1. private MediaPlayer mMediaPlayer = null
  2.          
  3.     public void onCreate() { 
  4.         super.onCreate(); 
  5. mMediaPlayer = new MediaPlayer(); 
  1. private MediaPlayer mMediaPlayer = null
  2.          
  3.     public void onCreate() { 
  4.         super.onCreate(); 
  5. mMediaPlayer = new MediaPlayer(); 
private MediaPlayer mMediaPlayer = null;
        
    public void onCreate() {
        super.onCreate();
mMediaPlayer = new MediaPlayer();
}

  構架完成MusicPlaybackService的基本架構後,我們要定義一些常用的控製接口了,其他模塊通過這些接口,可以控製音樂的播放,暫停,停止等功能。

  1. public<

    最後更新:2017-04-02 06:52:25

      上一篇:go J2EE空鏈接a標簽的處理方法
      下一篇:go linq to xml之增改刪查