旁白

音乐播放器 后记

后记

经过前面几个章节,音乐播放器已经完成了。

我们可以多放一点音乐进去,用它实际的播放歌曲。虽然功能基本上开发完了,但是少不了测试的环节。就目前我们的开发水平而言,我们可以通过多使用,来寻找软件的Bug。如果遇到崩溃、或者使用时发现的设计不合理的地方,就根据自己的想法来修改。逐步的让音乐播放器变成一个稳定实用的应用。

在功能规划的时候,我们还省去了不少实用的功能,也许在下一版本的音乐播放器中,我们就可以添加上其中的部分功能了。

在经过了“安豆计算器” “视频播放器” “蓝牙聊天应用”和“音乐播放器”以后,我相信大家一定对安卓应用开发有了较为深刻的理解了。至此,大家已经入门了,恭喜恭喜。

音乐播放器 第8节 桌面小工具

第8节 桌面小工具

桌面小工具是可以放置在主界面的、快速控制应用的小助手。例如我们的音乐小工具,它可以帮助用户在桌面上就完成音乐的暂停、播放、切换等操作,而不需要启动应用本身。

在安卓系统中,我们也常常叫它App widget

实现一个App widget要经过以下几个步骤,

  1. 创建一个App widget类,让它继承自AppWidgetProvider,例如AnddleMusicAppWidget类;
  2. 放在res\layout目录下,为App widget的界面定义一个布局,例如anddle_music_app_widget.xml
  3. res\xml目录下,定义一个App widget的说明文件,例如anddle_music_app_widget_info.xml
  4. AndroidManifest.xml文件当中,声明App widget

8.1 小工具框架的创建

对于使用Android Studio的用户来说,完成上面四个步骤的方法非常简单。因为Android Studio提供了简单的可视化向导来帮助我们。

  1. 在项目工程上点击右键,选择new->Wideget->App Widget就为我们创建好了开发用的模版,非常的简单方便。

  2. 在弹出的创建向导中,填上创建的参数,

    1. App widget取个名字-AnddleMusicAppWidget;
    2. 允许将App widget放到主界面上;
    3. 不允许App widget的显示区域动态调整;
    4. App widget占用3格宽1格高;

    这些参数最后将反应在res/xml/anddle_music_app_widget.info.xml这个配置文件当中,

运行起来,就可以开始将App widget添加到桌面了。

8.2 界面设计

现在,我们开始修改布局文件anddle_music_app_widget.xml,将App widget设计成如下的效果,

然后修改anddle_music_app_widget_info.xmlandroid:previewImage属性,让它指向的图片变成预览的效果图,

8.3 AnddleMusicAppWidget的重建

AnddleMusicAppWidget类负责App widget的逻辑响应。

8.3.1 AppWidget原理

继承AppWidgetProvider之后,开发者可以选择的实现onUpdate() onAppWidgetOptionsChanged() onDeleted() onEnabled() onDisabled()onRestored()等多个函数,

桌面上可以同时添加多个相同的App widget,系统会为每个App widget分配一个id,以便将来更新它们或者对它们进行别的操作时,能够正确的区分它们。

假设现在桌面上没有我们音乐播放器对应的App widget,此时添加一个的话,会依次触发,

如果此时继续添加,会接着触发,

如果系统要更新App widget的显示,也会触发,

此时删除一个,会触发,

此时再删除最后一个,会触发,

当设备开机,会触发,

App widget的设计框架中,每个显示出来的小工具界面,叫做RemoteViews,它是由如下方式创建的,

然后由RemoteViews提供的设置方法,来设置界面上每个元素的样子,比如某个TextView该显示什么文字,某个ImageView该显示什么图片,某个Button被点击后要触发什么操作等等。

然后,通过调用系统提供的AppWidgetManager.updateAppWidget(),将这个界面更新到App widget上面去,

所以AnddleMusicAppWidget要提供一个对外调用的接口,让其它组件可以在需要的时候更新小工具界面。

8.3.2 App widget的界面元素

前面提到了RemoteViews的创建方法。现在来详细介绍下界面上每个元素的设置方式,比如某个TextView该显示什么文字,某个ImageView该显示什么图片,某个Button被点击后要触发什么操作等等。

8.3.2.1 设置TextView的文字

如果创建RemoteViews的布局中有TextView控件,并需要设置内容,

8.3.2.2 设置ImageView的背景图片

如果创建RemoteViews的布局中有ImageView控件,并需要设置图片内容,

8.3.2.3 控件的其它属性

除了上面提到的控件和属性,控件的很多属性并专门的设置函数。不过RemoteViews为我们引入了更加灵活的设置方式。例如要设置Buttonbackground属性,

8.3.2.4 Button的响应

如果创建RemoteViews的布局中有Button控件,为它添加点击响应,

RemoteViews不能直接设置按钮点击后的操作,必须通过设置PendingIntent来实现。所以要在响应的模块中添加响应的逻辑。

8.3.3 AnddleMusicAppWidget实现

  1. 继承AppWidgetProvider,准备实现onUpdate()函数,并提供performUpdates()的静态函数作为其它组件刷新界面的接口,搭建的代码结构如下,

  2. 实现对界面元素的逻辑控制,

8.4 MusicService的改造

8.4.1 App widget触发MusicService

App widget的按钮被点击后,会触发隐式定义的Intent发送给MusicService。例如当下一首按钮被点击后,携带action-MusicService.ACTION_PLAY_MUSIC_NEXT的Intent将触发MusicServiceonStartCommand()函数。

我们可以在onStartCommand()函数当中接收到App widget要求的操作命令,进行相应的处理。

8.4.2 MusicService更新App widget

MusicService自身的播放状态发生变化的时候,比如开始播放、暂停播放,就需要更新App widget的显示,

8.5 App widget的初始化

当App widget被添加到桌面或者系统启动后让App widget在桌面显示出来,需要显示当前的播放信息。

假如MusicService已经在后台运行着,那么App widget只需要在onUpdate()的时候通知MusicService,让MusicService调用接口更新一下自己。

假如是刚刚开机,MusicService还没有运行起来,那么App widget需要让MusicService`先运行起来,再调用接口更新一下自己。

8.5.1 MusicService已经启动

  1. AnddleMusicAppWidgetonUpdate()中,向已经启动的MusicService发送一个广播,告诉它App widget需要被更新一下,

  2. MusicService在启动之后,要随时监听App widget发出的MusicService.ACTION_PLAY_MUSIC_UPDATE广播,一旦收到这个广播,就要响应去跟新App widget界面,

8.5.2 MusicService没有启动

如果设备刚开机,而桌面又添加了音乐的App widget,那么就让App widget在被加载到桌面上的时候(onEnabled()被触发时)启动MusicService

之后更新App widget的流程就和MusicService已经启动了的流程一致了。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

音乐播放器 第7节 播放音乐

第7节 播放音乐

音乐播放列表也准备好了,我们来播放音乐吧。完成后效果如下,

实现音乐的播放,我们需要播放界面和音乐服务两个方面的合作。

7.1 MusicService

前面我们已经为播放音乐的功能在MusicService中搭建好了框架,下面就要来实现它们了。这些实现接口的函数,我们在命名的时候都用上了xxxInner()这样的格式,例如playInner(),这是功能真正实现的地方。

7.1.1 创建播放器

MediaPlayer是实现音乐播放的核心,所有与播放相关的操作都是通过它来完成的。当MusicService运行起来的时候,就需要创建它的实例,退出时,要释放MediaPlayer实例,

7.1.2 监听器

MusicService提供注册监听器的接口,当播放音乐的状态发生变化的时候,能够实时的将当前的状态传递给关心的组件。

监听器的接口在之前的章节中,我们已经定义成了,

当某个组件(例如MusicListActivity)想要监听MusicServcie的时候,就要利用registerOnStateChangeListener()unregisterOnStateChangeListener()进行注册。MusicService把注册的监听器存储到数组列表当中,需要的时候取出来使用,

7.1.3 控制播放

播放、暂停、播放前一首、播放后一首的功能实现,并通过监听器通知监听者MusicService的状态变化。当前要播放的歌曲,将被放在叫做mCurrentMusicItemMusicItem数据结构中,

7.1.4 通知播放进度

除了控制播放,还要能向监听者报告当前音乐的播放进度。

我们设计的策略是:当音乐开始播放的时候,就每隔一秒钟报告一次当前播放的进度。要实现每隔一秒报告一次,可以通过创建Handler,每间隔一秒发送一条通知的消息来实现。

当音乐开始播放的时候,触发进度更新;当音乐暂停或者停止的时候,停止进度的更新;

7.1.5 连续播放

我们设计的播放规则是-将播放列表里的音乐按照次序自动播放,直到播完。所以我们要在每一首音乐结束后,自动播放下一首音乐。为此,要给播放器注册一个播放是否完成的监听器,当监听器被触发,开始下一首音乐的播放,

7.1.6 初始化待播音乐

MusicService启动之后,要先读取数据库中存储的播放列表,把第一首音乐作为默认的待播放的音乐,

7.1.7 添加到播放列表

关于我们定制的规则是这样的:如果用户选择添加一首音乐,那么添加后,自动播放该首音乐;如果用户选择添加一组音乐,那么添加后,自动播放该组音乐列表中的第一首。所以添加播放列表的代码还要做一些修改,

7.2 播放界面

根据我们的设计,播放界面位于音乐列表的下方。这里面将包含播放、暂停、上一首、下一首、播放进度展示、播放进度拖动等功能。

7.2.1 界面修改

为了实现各种控制功能,我们需要修改界面布局方式,将这个区域叫做播放控制区域。界面布局如下,

7.2.2 控制区域组件

MusicListActivity创建的时候,我们将获取播放器控制区域的组件,并添加对按钮进度条的响应,

7.2.3 禁用和启动控制区域

当音乐列表进入modal状态或者播放列表为空时,要让控制区域不可操作,

7.2.4 注册监听器

更新播放状态,在取得MusicService的访问接口后,注册监听器,更新控制区域的信息,

7.2.5 实现监听器

实现监听器监听MusicService的变化,


至此,整个应用的音乐播放功能就完整的实现了。下面就播放一下你准备好的歌曲吧。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

音乐播放器 第6节 存储多首音乐

第6节 存储多首音乐

我们已经能够存储单首音乐了,接下来我们开始添加一次存储多首音乐的界面设计。

当用户长按某首音乐项的时候,将进入ListView的多选modal状态,我们叫它模式框。

6.1 ListView的选择模式

在理解这个模式框之前,我们要先进一步了解一下ListView

ListView本身带有单选和多选功能,也就是说在单选模式下,它能够记住当前选中的唯一的列表项;在多选模式下,它能够记住目前选取的所有的列表项。

这种选择模式共有4种CHOICE_MODE_NONE CHOICE_MODE_SINGLE CHOICE_MODE_MULTIPLE CHOICE_MODE_MULTIPLE_MODAL

使用setChoiceMode()函数,就可以设置ListView的选择模式。

6.1.1 CHOICE_MODE_NONE

这是ListView默认的选择模式,当使用者点击列表项后,不会将任何点击的列表项当成被选择上的列表项。

6.1.2 CHOICE_MODE_SINGLE

使用这个选项模式,当使用者点击列表项后,会将最后一次点击的列表项当成被选择上的列表项。

每次点击列表项,可以在它的监听函数中得知哪个列表项被点击了,

以后就可以通过ListViewgetCheckedItemPositions()获取被选中的数据项的位置列表,

例如,当列表项按照序号为3->2->1-7的顺序被点击后,7将被记录下来,作为当前被选中的数据项。调用getCheckedItemPositions()后,将得到一个键值对,里面只会记录,

这里为了看到被选中的效果,采用了Android SDK提供的android.R.layout.simple_list_item_single_choice布局,作为列表项的布局。

6.1.3 CHOICE_MODE_MULTIPLE

使用这个选项模式,当使用者点击列表项后,会将所有点击的列表项当成被选择上的列表项。

每次点击列表项,可以在它的监听函数中得知哪个列表项被点击了,

通过ListViewgetCheckedItemPositions()获取被选中的数据项的位置列表,

例如,当列表项按照序号为3->2->1->7的顺序被点击后,3 2 1 7将被记录下来,作为当前被选中的数据项。调用getCheckedItemPositions()后,将得到一个个键值对,里面会记录,

这些记录,将按照列表项的位置从小到大排列。

如果用户在已经被选上的数据项上,又再次点击,这将取消对它的选择。例如,当列表项按照序号为3->2->1->7->2->1的顺序被点击后,就变成了,

对于被取消了选择的列表项,并不会从记录中移除,而是改变它的值成false

这里为了看到被选中的效果,采用了Android SDK提供的android.R.layout.simple_list_item_multiple_choice布局,作为列表项的布局。

6.1.4 CHOICE_MODE_MULTIPLE_MODAL

CHOICE_MODE_MULTIPLE_MODAL模式中,用户必须通过长按任意一个列表项,进入多选模式,否则不能进行多选。

这个模式的使用与CHOICE_MODE_MULTIPLE类似,不过它需要设置一个ActionMode,这样才能在长按列表项后,改变ActionBar的菜单栏,打开多选的菜单。

  1. 实现ListViewMultiChoiceModeListener接口,

  2. 使用创建的接口,传递给ListView

CHOICE_MODE_MULTIPLE_MODAL模式下,对列表项的点击,是在ListView.MultiChoiceModeListeneronItemCheckedStateChanged()函数中响应的。

这里为了看到被选中的效果,采用了Android SDK提供的android.R.layout.simple_list_item_multiple_choice布局,作为列表项的布局。

6.1.5 关于单选和多选的状态背景

前面的演示中,分别对不同的选择模式,使用了不同的数据项布局,

我们也可以选择自己的布局来实现。但是不管使用什么布局,我们都希望被选上的列表项有特别的效果,和其他没有被选上的数据项区别开。这里有两种方案,

  1. 使用实现了Clickable接口的控件或者布局,作为数据项的布局。例如前面我们使用的android.R.layout.simple_list_item_multiple_choice android.R.layout.simple_list_item_single_choice等,它们本身就是CheckedTextView。当点击它们的时候,它们就自带了勾选框,可以看到效果。

    所以,可以选择CheckBox Switch这类同样实现了Clickable接口的布局来表现;也可以自定义一个实现了Clickable接口的布局。这里用android.R.layout.simple_list_item_multiple_choice的实现来举个例子,

  2. 给数据项的布局使用一个Selector,告诉这个布局,当它被activated以后要怎么显示。比如,定义一个布局custom_item_layout.xml,

    给这个布局设计一个selector,增加对android:activated属性的设置,

    使用这个这个布局,

    当我们多选的时候,那些选中项,就变成黄色,看到如下的界面,

6.2 选择多首音乐

接下来,我们将上面学到的方法应用到我们当前的项目中。

  1. 创建music_choice_actionbar.xml菜单项,

  2. 定义一个MultiChoiceModeListener,将菜单项和菜单项被点击后的响应添加进去,

  3. 将音乐列表设置成多选modal模式,

  4. 为了便于观察哪些音乐处于多选状态,我们让被选择的数据项处于高亮状态,

    1. res\drawable目录下,定义一个高亮的selectormenu_item_selector

    2. 给音乐项的布局文件music_item.xml设置上高亮的selector

  5. 增加添加到播放列表的功能,


    现在长按数据项,点击看看,已经能够成功的把选上的音乐添加到播放列表了。


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

音乐播放器 第5节 播放列表的存取

第5节 播放列表的存取

关于播放列表的存取需要三个组件的协同配合,

  1. MusicListActivity:让用户选择多首或一首音乐,将用户的选择项,传递给MusicService
  2. MusicService:接收到添加列表的请求后,把数据交给PlayListContentProvider,进行存储;
  3. PlayListContentProvider:将播放列表存储到SQLite数据库中;

5.1 PlayListContentProvider的实现

自定义的ContentProvider与系统自带的ContentProvider在设计和用法上都是一样的。

5.1.1 地址设计

我们首先来学习一下ContentProvider的Uri地址。

任何对数据的操作范围无外乎,

  • 单条数据的操作:每一次操作增加一条数据,删除一条数据,修改一条数据,查询特定一条数据的详细内容;
  • 多条数据的操作:每一次操作增加多条数据,删除多条数据,修改多条数据,查询符合某个特征的多条数据;

例如,我们可能会向书架上“一次放一本书”-单条增,“一次取下一本书”-单条删,“一次问书架上有多少书”-多条查。

因此,仿照网站地址的设计方式,我们可以用如下的“网络地址”来表达我们希望进行的对ContentProvider的操作是针对单一一条数据还是同时多条数据:

  • xxx.xxx.xxx/items/1:针对单一一条数据,最后的数字代表特定一本书的编号(也可以使用书的名字来代替);
  • xxx.xxx.xxx/items:针对多条数据;

“网站”只要看到以上的格式就知道,要操作的是一条数据还是多条数据了。

每个ContentProvider就是一个“网站”,每个网站都有自己的“网址”。安卓系统为这个“网址”设计了如下的结构,

  1. scheme:固定为“content”,相当于一个网址的“http”;
  2. authority:由开发者自己确定,通常把它写成这个ContentProvider的包名,例如“com.anddle.mycontentprovider”,它就相当于网址的地址“www.google.com”;
  3. path:根据查询内容的逻辑,由开发者自己决定,通常要分成多条数据和单一数据两类;

典型的例子就像这样,

这里面,
scheme:“content”
authority:“com.anddle.mycontentprovider”
path:“items”或者“items/1”

只要定义好了前面两种原则,外界(其他组件或者其他应用)就可以获取到ContentProvider中的内容了。

Urischeme字段是固定的,使用content:

authority定义成程序的包名com.anddle.mycontentprovider

path就像是网站内部的分类,依据网站的逻辑进行划分。
假设我们的ContentProvider提供书籍book和文件file两种内容的查询操作。而每种类型都可以进行单一数据的操作和多条数据的操作。

例如,

  • 操作所有书的信息:content://com.anddle.mycontentprovider/books;
  • 操作某本特定书的信息:content://com.anddle.mycontentprovider/books/8

针对我们这个音乐的例子,它的URI地址可能就是,

  • 操作多首音乐:content://com.anddle.anddlemusicprovider/songs;
  • 操作某首特定音乐:content://com.anddle.anddlemusicprovider/songs/8

5.1.2 创建ContentProvider

  1. 继承ContentProvider类,会要求我们实现getType() insert() delete() update() query() onCreate()等接口,

  2. 定义提供给其他组件使用的“网络地址”URI,这里我们把它们定义成content://com.anddle.PlayListContentProvider/songs

  3. 在响应函数中,根据Uri,做对应的操作,例如insert()函数中,就要实现对数据增插入的真正操作,不过现在我们把操作数据库的操作留到后面来讲解

    类似的,其他delete() update() query()实现的函数做相同的处理。

  4. 对于getType(), 需要为每一种类型的Uri返回一种数据类型-MIME type,告诉调用者,当前这种Uri可以处理什么类型的数据。

    它的格式型如type\subtype,有很多知名的MIME type类型,例如application/pdf image/jpeg等等,可以在网上查找到公开的MIME type类型有哪些。也可以自定义自己应用支持的特殊MIME type类型。

    这里我们就返回一个空值,

至此,一个ContentProvider的就完成了。不过它现在还没有添加上真正可以存储数据的功能。

5.1.3 声明ContentProvider

千万不要忘记,在应用的AndroidManifest.xml文件中,声明新添加的ContentProvider

这里的android:authorities属性值,就要填写定义MyContentProvider时,代码中的那个,

android:exported属性如何设置成true,说明这个ContentProvider可以被其他应用使用(就像一个公共网站,可以被任何人访问),如果设置成false,说明它只能被自己所在的应用使用(就像一个内部网站,只能在公司内部访问)。

5.1.4 使用Android Studio创建

上面介绍了创建ContentProvider的原理,如果使用Android Studio做开发,可以更加方便的为我们创建ContentProvider相关的代码,

  1. 在项目上点击右键,选择New->other->ContentProvider

  2. 在弹出的对话中,填写上ContentProvider的名字和自定义的authorities,并选择Finish

创建成功后,自定义的ContentProvider框架就自动帮我们实现了;同时,替我们在AndroidManifest.xml文件中完成了对它的注册。剩下的就是要我们向里面添加自己的逻辑代码了。

从这里也能看到使用Android Studion开发环境的便利之处。

5.1.5 数据库存储

创建了ContentProvider后,它还只是虚有其表,不能保存任何数据。要保存数据,通常会让它使用数据库的方式实现。

  1. 假设数据库的名字叫做playlist.db,播放列表将存储在该数据库名叫playlist_table的表中。

    该表的结构如下,

    id name last_play_time song_uri album_uri duration
    1 国歌 0 xxxx xxxx 13908888
    2 小苹果 0 xxxx xxxx 13908888
    3 回家 0 xxxx xxxx 13908888

    其中,这些字段对应的数据类型分别是:
    id:自增的int型数据,作为每条数据的主键,每插入一条数据,该值将被数据库自动分配;
    name:字符类型的数据,存放歌曲的名称;
    last_play_time:long型数据,以毫秒为单位,记录该音乐上次播放到的时间;
    song_uri:字符型数据,记录每首音乐的uri地址,例如xxxxx;
    album_uri:字符型数据,记录每首音乐封面的uri地址,例如xxxxx;
    duration:long型数据,以毫秒为单位,记录该音乐一共可以播放的时长;

  2. 安卓系统为我们提供了一个方便的使用SQLite数据库的工具类SQLiteOpenHelper,通过它来创建、更新或者删除SQLite数据库。

    1. 继承SQLiteOpenHelper,并定义数据库中使用的表名称和字段;继承的时候,需要我们一定实现onCreate()onUpgrade()函数,前者实现对数据库的创建,后者告知开发者,数据库的版本有变化,让开发者有机会重新组织已存储的数据,

    2. 创建数据库和更新数据库;

    3. PlayListContentProvider的增删改查工作,将依赖于我们刚创建的DBHelper

      1. 创建DBHelper

      2. 添加播放列表的操作,每次向数据库插入一条信息,

      3. 删除播放列表的操作,我们的音乐播放器在批量添加歌曲到播放列表的时候,首先要清空所有的播放歌曲列表,并没有单独删除某一条歌曲的需要,所以在进行删除操作的时候,我们只需要将整个playlist_table清空就好了,

      4. 修改播放列表的操作,当音乐在播放的时候,我们需要记录下档前音乐的播放进度,就要把这个信息更新到现有的数据库当中,

      5. 查询播放列表的操作,当用户想查看当前播放的音乐列表时,查询播放列表中有哪些音乐,


      至此,一个功能完整的ContentProvider就实现了。

5.1.6 使用自定义ContentProvider

无论是使用应用自己的ContentProvider还是使用其他应用提供的,它们的使用方式都和使用系统提供的ContentProvider一样,

  1. 添加一条数据数据:通过ContentResolver获取访问ContentProvider的入口,使用ContentValues添加要插入的数据;

    通常会返回指向刚成功插入的这条数据的Uri(内容就如content://com.anddle.PlayListContentProvider/songs/8)。

  2. 删除一条数据:通过ContentResolver获取访问ContentProvider的入口,使用Uri删除指定的数据;

  3. 修改一条数据:通过ContentResolver获取访问ContentProvider的入口,使用Uri更新指定的数据,要修改的数据放在ContentValues当中;

  4. 查询某一类的数据(或者特定某条数据),

  5. AndroidManifest.xml文件中,要添加上读写磁盘的权限,这样才能成功的读取和保存数据,

注意,在删改查的操作中,还会使用诸如where sortOrder keywords searchKey这样的参数,它们是辅助ContentProvider查询特定数据时用的。

5.2 MusicService的配合

MusicService对外提供添加播放列表的接口,对内要管理PlayListContentProvider。它提供了下面的接口,

  • addPlayList():添加播放列表。这里添加列表应该有两种形式,一种是一次性添加多首音乐,一种是一次就添加一首音乐。

  • getPlayList():获取播放列表

播放列表要保存到一个数组里面,所以我们要创建一个mPlayList实现列表的保存,

5.2.1 添加播放列表

addPlayList()应该有两种形式,一次添加一首音乐和一次添加多首音乐。

5.2.1.1 添加一首音乐

判断mPlayList是否保存了音乐,需要对MusicItem做特别的处理:给它定义一个比较的准则–什么情况下两个相比较的内容是相同的。我们这个例子当中,应该是音乐的Uri相同,则认为两者相同。重写MusicItemequals()方法,当使用mPlayList.contains(item)判断的时候,会调用到这个重写的方法进行比较,

5.2.1.2 添加多首音乐

5.2.2 获取播放列表

为了获取播放列表在ContentProvider启动的时候,需要它将数据库中现有的列表,加载到mPlayList当中,

之后在需要的时候,直接将mPlayList返回就可以了,

5.3 主界面播放列表的添加

MusicListActivity有两种方式添加播放列表:

  1. 单击音乐项,将单首音乐添加到播放列表中;

  2. 长按音乐项,界面切换成ListView的多选模式,将多首音乐用替换掉方式添加到播放器当中;

这里我们先来实现单击音乐项的添加,多选模式我们放到后面单独的章节来介绍。

修改点击列表的监听器,通过MusicService提供的接口,把要添加的音乐交给MusicService处理,

接下来开始实现显示播放列表的功能,给MusicListActivity做一个Menu菜单,定义菜单的xml文件main_menu.xml

将菜单添加到MusicListActivity当中,

当用户点击该菜单的时候,弹出一个自带列表的对话框,将播放列表显示出来,


/**************************************************************************/
* 版权声明
* 本教程只在CSDN安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/

音乐播放器 第4节 播放服务的搭建

第4节 播放服务的搭建

播放音乐的服务-MusicService是整改音乐播放器的核心,它将播放界面和实际的播放功能连接在一起。

4.1 MusicService的接口

它对外要提供两类主要的功能,播放音乐的控制接口和播放列表的存取接口。

4.1.1 播放音乐的控制接口

根据功能的定义,我们认为控制接口应该包括:

MusicListActivity为了获取MusicService的状态,还需要设置监听器,当MusicService有变化时,能主动通知到MusicListActivity。所以要有监听函数:

监听时要获取的信息包括:

  1. 播放进度的改变;
  2. 音乐开始播放;
  3. 音乐停止播放;

所以设计监听器的接口为,

当然,还可以设计获取更多的信息,这里我们就简单的获取这几种简单的信息。只要知道了设计的原理,我们就可以在以后随心所欲的改造。

4.1.2. 播放列表的存取接口;

MusicService通过操作PlayListContentProvider来实现对音乐列表的存取。MusicService对外提供这样的接口:

4.2 Service的使用

安卓系统的Service按照创建的方式进行分类,有两种:启动Service-start Service,绑定Service-bind Service。前者使用startService()运行,后者使用bindService()运行。

4.2.1 Start Service

其他组件通过调用startService()函数将Service运行起来,再通过调用stopService()函数让其停止运行。

单纯的使用这种形式的Service最为简单,它和它的调用者之间没有什么联系,调用者只是负责启动它和停止它,或者在启动它的时候通过Intent传递一点数据,除此之外,两者没有数据交换、没有其他的功能调用,这两个组件之间基本上互不影响。

设计这样的一个Service需要,

  1. 继承Android SDK提供的Service类,重写onBind()函数,让它返回空值;

  2. AndroidManifest.xml中,声明新创建的Service

使用这种Service也很简单。假设Activity A中有个按钮start,点击之后就调用startService;还有个按钮B-stop,点击之后就调用stopService

这里运行Service的时候,是通过Intent明确指定被运行的Service。这种明确指定启动哪个Service的方式叫做Service的显示调用。与之对应的还有隐式调用,通过action来启动。

4.2.2 Bind Service

其他组件通过调用bindService()绑定Service,让它运行起来;再通过调用unbindService()解除绑定。

这种形式的Service与调用者之间通常有功能调用或者频繁的数据交换,Service会提供接口给其它模块调用,

设计这样的一个Service需要,

  1. 继承Android SDK提供的Service类,

  2. 实现一个自定义的Binder,让它这个继承Binder类。

    Binder可以将Service与调用者联系起来,在Binder中提供的方法,就是Service对外提供的方法。

    组件和Service之间的调用是通过Binder来进行的。我们可以把Binder看作是一个连接其他组件和Service的桥梁,它的实现原理是什么,我们暂时不用去关心,只要知道这样用就可以了。

  3. AndroidManifest.xml中,声明新创建的Service