音乐播放器 第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安豆网发布,其他网站出现本教程均属侵权。
/
**************************************************************************/