天天看点

OPhone程序开发入门之音乐播放器

ophone平台提供了完整的多媒体解决方案。为开发者提供了统一的,简单易用的开发接口。本文首先介绍了ophone平台的多媒体框架,然后详细介绍了 在ophone平台上开发音乐播放程序所需的基本知识。通过一步一步构建一个简单的音乐播放器示例程序,来帮助读者了解具体的开发过程。该示例涵盖了application,activity,service,intent,broadcast  receiver等基本概念,使读者对ophone程序的开发有一个全面的了解,进一步巩固和熟悉这些基本概念。最后介绍了如何利用mat工具分析ophone

程序。

        本文适合ophone平台开发的初学者阅读。(作者:cmri 孟钊)

ophone平台的多媒体架构

        在开始构建我们的示例程序前,先让我们大概了解一下ophone平台的多媒体框架。

OPhone程序开发入门之音乐播放器

图   一

       图一是ophone平台的整体框架结构,从图上我们可以看出ophone平台大致可以分成以下几个层次:

最上层是application层。它包含了主屏,电话,浏览器,地址本等核心的应用程序。我们将开发的音乐播放器也属于这一层。

第二层是application  framework层。这一层为开发者提供了完整的编程接口。多媒体部分提供了mediaplayer,  mediarecorder等接口。同时mediaprovider,mediascanner等系统服务也对媒体文件的管理提供了支持。本文将重点介绍 它们的使用。

第三层是library层,  它由一系列的c/c++库组成,这些库的能力通过jni封装成java接口,由application  framework层提供给开发者。多媒体系统库opencore,它是ophone多媒体的核心,来源于packetvideo。它非常复杂,提供了完 整的多媒体解决方案。

最底层为linux kernel和驱动,负责与硬件的数据交互等。

OPhone程序开发入门之音乐播放器

  图二说明了在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:

OPhone程序开发入门之音乐播放器

        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信息,同时还指出了可疑的内存泄漏的对象。

OPhone程序开发入门之音乐播放器

       大家可以根据mat提供的详细heap信息,查找漏洞了。

附录:源代码:

uploads/file/musicplayerdemo.zip