1. 引言
随着智能手机和终端的普及,相继出现了各类APP。基于Android平台开发的手机应用有着基本的通用性,可以在各个厂商的手机中普遍安装和使用 [1] 。目前,国内外主流的基于Android平台的音乐播放软件有天天动听、酷狗音乐、QQ音乐等。但大多数一味的追求外观漂亮,功能强大,造成用户视觉疲劳,手机资源占用太多,多任务操作时响应较慢,用户体验较差 [2] [3] 。本文旨在研究开发一款基于Android平台的实用的个性化音乐播放客户端。
2. 系统需求分析
1) 总体需求分析
针对Android手机用户,设计与实现一款简洁大方、易于操作的个性化音乐播放客户端,能够支持MP3、WAV等多种音频格式。
2) 功能需求分析
基本播放控制功能:播放/暂停、选择上一首/下一首、选择播放模式、显示歌词、拖动播放进度条、打开文件管理、后台播放。
侧滑菜单功能:扫描歌曲、切换播放模式、换背景、睡眠、退出、系统设置。
歌曲索引及定位功能:当内存中歌曲数量较多时,本文提供两种查找方式:a) 关键字查找:根据用户输入的文字进行关键字索引。b) 九键首字母查找:以字首字母为索引,并定位到相应歌曲。
3) 性能需求分析
为了在基于Android的手机上达到良好的用户体验,要求:a) 响应速度快,歌曲播放/暂停、选择上一首/下一首,拖动进度条等操作均不能超过5 s;b) 人性化的播放界面,体现出简洁大方、易于操作的特点;c) 无缝衔接。程序运行时,要保证一些突发事件的处理,如电话、短信等,场景切换过程中需要做到无缝衔接 [2] [4] 。
3. 系统设计与实现
3.1. 软件架构设计
图1为本文系统的软件架构图,分为音乐播放界面层和音频处理层两层。界面层与用户直接交互。音频处理层对音乐文件进行处理,播放SD卡中的mp3、wav、midi、wma等音乐文件。首先在数据提取层将音乐文件从SD卡中提取出来,传送到数据处理层进行封装,并将其传送到解码层,使用相应的解码器解码文件,并进行播放。双箭头表示两方进行数据交互,单箭头表示单向传输数据。
3.2. 系统功能设计
根据功能需求分析,对系统进行功能设计。整个音乐播放软件划分为四大功能模块:播放控制模块、播放列表模块、文件管理模块、搜索模块。系统功能结构图如图2所示。

Figure 2. System functional structure diagram
图2. 系统功能结构图
根据功能结构图,设计出其内部实现的框架结构图,如图3所示。图中虚线代表依赖于关系。
1) Activity:与用户进行交互的界面;
2) Model类:存放歌曲信息的类;
3) Service:用户不可见,实现歌曲的播放/暂停等功能;
4) Broadcast:用来实现消息的广播;
5) B11层:用来实现接口;
6) DAL:依赖于B11层,存放数据库的操作类,用来创建、实现、删除数据库。
3.3. Android开发环境的搭建
本文开发的音乐播放软件是在Android平台上,Java语言编程,所需软件和开发工具有JDK、Android Studio、Android SDK。
环境变量的设置:将JDK开发包中的bin文件夹的绝对路径、SDK中的tools文件的绝对路径放到“Path”变量中;添加JDK中的lib以及demo文件夹的路径到“CLASSPATH”变量。
建立一个新的工程,包括三个文件夹:
1) java文件夹:存放工程的.Java文件。
2) res文件夹:包含整个工程的所有资源,drawable(图标)、layout(布局文件)、values(常量)等。
3) AndroidManifest.xml文件:包括了程序中要用到的全部活动、服务、接收器,跟Android安全相关的文件 [5] [6] 。
3.4. 系统界面设计与实现
根据系统需求分析,界面的设计:要求界面简洁,因此采用线性布局的方式;使用触控方式对界面进行操作;对不同的功能使用不同的屏幕表示。
1) 播放控制主界面
设计模型如图4所示。

Figure 4. Layout of playback control home interface
图4. 播放控制主界面布局
数字说明:
① TextView控件,显示歌词信息,如歌曲及艺术家名称等。
② 显示歌词,通过匹配的歌词在此处滚动播放。
③ SeekBar控件,进度条。
④ Button控件,用户点击该按钮切换到上一首歌曲。
⑤ Button控件,播放/暂停按钮。
⑥ Button控件,下一首按钮。
⑦ 无歌词提示文本,平时处于隐藏状态,当没有找到匹配歌曲时显示。
⑧ 声音设置按钮。
⑨ 我的最爱快捷键,将该歌曲加入“我的最爱”表中。
⑩ Button控件,当前歌曲表单对话框按钮。
2) 播放列表界面
使用ListView、TextView、ImageView进行线性布局,其中TextView显示列表名称,如图5所示。
3) 侧滑菜单界面
主要使用ListView、TextView、ImageButton、Button、EditText、ImageView进行布局,使用第三方的开源库SlidingMenu来实现侧滑菜单的功能。侧滑菜单界面布局如图6所示。
mSlidingMenu = new SlidingMenu(this); mSlidingMenu.setTouchModeAbove(SlidingMenu.TOUCHMODE_MARGIN);
mSlidingMenu.setMode(SlidingMenu.RIGHT); mSlidingMenu.setShadowWidthRes(R.dimen.shadow_width);//设置阴影的宽度 mSlidingMenu.setShadowDrawable(R.drawable.shadow);//滑动菜单的阴影效果 mSlidingMenu.setBehindOffsetRes(R.dimen.slidingmenu_offset);//设置偏移量 mSlidingMenu.setFadeDegree(0.35f); mSlidingMenu.attachToActivity(this, SlidingMenu.SLIDING_CONTENT);/ /设置布局 mSlidingMenu.setMenu(R.layout.frame_menu);

Figure 5. Layout of playlist interface
图5. 播放列表界面布局

Figure 6. Layout of side slide menu interface
图6. 侧滑菜单界面布局
4) 搜索界面
主要使用EditText、ImageView、ListView进行线性布局,如图7所示。
3.5. 播放功能的实现
在Android中要实现某个功能,首先需要实现与用户进行交互的控件,对用户的触摸发生响应;其次是各个界面之间的切换,需要对进程的生命周期进行控制和管理等。音乐播放功能主要使用MediaPlayer类来完成。
1) 创建与销毁
使用new或create()创立一个该类的实例对象。release()释放不需要的内存。
2) 初始化
首先使用重载的setDataSource()将对象转变到initialized形态,接着转变为prepared形态。此时MediaPlayer对象获得歌曲时长等信息,使用setVolume设定播放器的音量。
3) 播放、暂停与停止
当对象是prepared状态时,用start()转变成started状态,用pause()转变成paused状态。调用isPlaying()判别对象是不是位于started状态。
4) 进度条
通过seekTo()方法来控制对象的媒体时间,onSeekCompleteListener来监听进度条的播放进度。
5) 错误处理
当播放歌曲时会发生各种错误,例如文件格式错误,或用户操作错误等。为了应对这些情况,MediaPlayer对象设置了onErrorListener。有错误时,就用onError()使对象进入到error状态。如果想恢复播放器的状态,就使用reset()使对象再次进入idle状态。
4. 关键技术分析
4.1. 数据库设计与分析
数据库设计会直接影响到系统运行的效果。本文用到的数据库主要是SQLite,一个支持SQL语言标准的开源的Android数据库,只需要几百KB的内存,可以在任意操作系统中使用。
4.1.1. 数据库存储设计
根据系统的功能结构图,以及对所需数据的分析,
数据库主要设计以下表格:
1) 歌曲信息表,主要用来存放歌曲id,歌曲名称,艺术家,专辑路径,大小,歌词等信息,如表1所示。
2) 已有播放列表,如表2所示。
3) 当前播放列表,与表2类似。
以上数据库之间的关系如图8所示。
4.1.2. 数据库的实现
在开发过程中,通过继承SQLiteOpenHelper类来建立和管理数据库。参数context用来判断打开还是创建数据库,name表示数据库名称,factory用来创建游标,version代表当前database的版本号。
通过构造方法来建立数据库:数据库不存在时,建立且打开数据库,再执行创立表的操作;数据库存在时,检测从构造函数传入的版本号,使用onUpgrade()更新数据库和版本号,再重新调用onCreate创建新的数据库信息。关键代码:
public class DatabaseHelper extends SQLiteOpenHelper { private static SQLiteDatabase mDb; private static DatabaseHelper mHelper; private static final int DB_VERSION = 3; private static final String DB_NAME = musicstore_new private static final String TABLE_ALBUM = album_info private static final String TABLE_ARTIST = artist_info private static final String TABLE_MUSIC = music_info private static final String TABLE_FOLDER = folder_info private static final String TABLE_FAVORITE = favorite_info /**创建主界面中的五个表格*/
@Override public void onCreate(SQLiteDatabase db) { db.execSQL(create table + TABLE_MUSIC + (_id INTEGER PRIMARY KEY AUTOINCREMENT + songid integer, albumid integer, duration integer, musicname varchar(10), + artist char, data char, folder char, musicnamekey char, artistkey char, favorite integer)); db.execSQL(create table + TABLE_ALBUM+ (_id INTEGER PRIMARY KEY AUTOINCREMENT, + album_name char, album_id integer, number_of_songs integer, album_art char)); …… }
@Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { if (newVersion > oldVersion) { db.execSQL(DROP TABLE IF EXISTS +TABLE_ARTIST); db.execSQL(DROP TABLE IF EXISTS +TABLE_ALBUM); db.execSQL(DROP TABLE IF EXISTS +TABLE_MUSIC); db.execSQL(DROP TABLE IF EXISTS+TABLE_FOLDER); onCreate(db);/*创建新的数据库信息*/ }
}
……
}
4.2. 消息处理机制
由于音乐播放界面和其他控件之间的通讯十分频繁,通过Android的Handler消息机制来处理。当处理视图对象时,需要对控件进行实例化,然后将控件ID与对应的配置文件进行绑定,并设置监听器进行监听,最终通过重写OnClick()响应用户的操作,并进行状态刷新 [2] [7] 。消息处理的流程:
1) 封装消息对象,将指定的Handler以及各种回调函数等进行封装;
2) 使用Handler的sendMessage()来发送消息;
3) 把消息加入到Looper的消息队列;
4) Looper则将循环的从消息队列中拿出消息,并进行处理,且删除处理过的消息。
以“扫描歌曲”为例,在布局文件中定义按钮“scanBtn”:
在activity中,OnClick()响应用户的操作:
public void onClick(View v) { switch (v.getId()) { case R.id.keyboard_switcher: if (mIsT9Keyboard) {// 若T9隐藏,打开T9 mKeyboardSwitcherIv.setImageResource(R.drawable.keyboard_switch); mSearchInputEt.setHint(请输入简拼或全拼); mSearchInputEt.setInputType(InputType.TYPE_NULL); mInputMethodManager.hideSoftInputFromWindow( mSearchInputEt.getWindowToken(), 0); // 隐藏输入法 mKeyboardLayout.setVisibility(View.VISIBLE); }
break;
} }
5. 系统测试
测试建立在硬件系统良好的前提下,根据用户对系统功能及界面的需求制定相应的测试用例,得到测试结果 [7] [8] 。测试环境:Android模拟器,4.0.1,手机。
5.1. 功能测试
播放和进度条测试:效果图如图9所示,播放进度条在播放时可以自行进行刷新,并显示播放时间,左边显示为已经播放时间,右边显示剩余时间。上面是Seekbar正常状态,下面是其被选中时的效果。

Figure 9. Rendering of playback and progress bar
图9. 播放和进度条效果图
后台播放测试:从图10可以看出后台播放时出现的后台图标,以及用户可以在下拉框中看到播放器的播放基本控制键,方便用户进行操作。
播放列表测试:图11中显示的常驻快捷播放栏,上方是当前数据库中的音乐列表,当前播放的歌曲有图片提示。
侧滑菜单测试:如图12所示,当用户向左滑动时,可以看到出现的菜单栏。
常驻菜单栏及主界面测试:如图13,主界面将用到的五个表以按钮的形式呈现给用户,点击相应的按钮即可进入对应的列表。下方的菜单栏和图12中的菜单栏是一致的。
播放界面效果图如图14所示,达到了预期效果。
5.2. 性能测试
本文对手机预装音乐播放软件进行了100次测试。音乐播放流畅,音质清晰,系统的稳定性满足用户需要。提取每十次测试的系统响应时间平均数据,画出折线图,并与市场上其他音乐播放软件进行对比,结果如图15所示。实折线表示Android市场上一般音乐播放软件的响应时间,虚折线表示本文系统的响应时间。可以看出,系统的响应时间达到了要求,并体现了其响应迅速的优势 [9] 。

Figure 15. Comparisons of system response time
图15. 系统响应时间对比折线图
6. 结论
在分析国内外已有Android平台音乐播放软件的基础上,结合对用户的调查,得出本文音乐播放软件的功能和性能需求,对系统进行了总体的设计,并对各个模块进行详细的设计,讨论了数据库、消息处理机制等关键技术。以Android为平台,JAVA语言为主导,实现了UI界面及功能。最后对系统进行了功能及性能上的测试,并与市场上音乐播放软件进行了对比。测试表明,该音乐播放软件界面清晰简单,操作方便,占用手机资源较少,响应速度快,达到了预期的目标,具有较大的市场空间。实际应用中,还可以在流畅交互方面进一步研究完善 [10] 。
基金项目
安徽省大学生创新创业训练项目(201710363159)。
参考文献
NOTES
*通讯作者。