阅读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之增改删查