作者 Chaoqian.chen
总体上Music App分为UI界面、服务两个模块,其中关于音乐文件的播放都由服务负责,服务配合AIDL使用的,界面绑定服务后可以拿到服务里所有参数及状态进行UI刷新。
A. 界面模块:
1、主界面MusicMainActivity:
主界面主要负责分类显示音乐文件,以及对音乐文件的各类操作。
MusciAllFragment:显示所有单曲。
SingerFragment:根据歌手分类显示。
AlbumFragment:根据专辑分类显示。
RrecentlyPlayFragment:显示最近播放的歌曲
PlayListFragment:显示用户收藏、录音以及自己创建的播放列表。
2、音乐播放界面MusicPlayingActivity
主要负责展示具体某一首歌曲的详细信息以及播放操作等。
3、音乐搜索界面SearchMusicActivity
输入内容后自动从本地的音乐文件的音乐名,专辑名,歌手名去匹配,匹配后显示到搜索列表里。
4、音乐列表界面MusicListActivity
负责显示播放列表里的歌曲,跟单曲差不多。
5、编辑界面EditMusicActivity
批量编辑音乐文件,包括删除和批量添加到播放列表。
6、PlayingFromUriActivity
负责接收外来资源的播放界面,逻辑跟播放界面一样。
B. 服务模块:
启动主界面后绑定服务,所有界面在onResume里根据服务是否存在判断是否进行绑定,在onStop里根据通知栏是否存在判断是否进行解绑(因为很多时候写在onDestroy里执行不到解绑服务的,导致服务永生不死,不符合谷歌规范)。由于服务绑定的都是单个Activity,若结束当前绑定的Activity,服务则会自动解绑执行onUnbind方法。
为了让服务能一直播放音乐…所以调用服务播放音乐时,就会调用startService为当前服务进行续命,并显示通知栏。所以就算杀掉APP,服务也会继续后台播放,若关闭通知栏则调用stopSelf杀掉服务。若此时点击通知栏调出UI播放界面后(此时的服务是之前续命的服务,并没有绑定任何Activity),再关闭通知栏,则会先stopSelf再发送一个广播通知当前Activity进行重新绑定服务。
C. 具体实现:
进入界面后首先要做的就是扫描本地所有音乐文件:
String[] paths = new String[] {Environment.getExternalStoragePublicDirectory(
Environment.DIRECTORY_MUSIC).toString()};
// String[] paths = new
// String[]{Environment.getExternalStorageDirectory().toString()};
MediaScannerConnection.scanFile(c,paths, null, new OnScanCompletedListener() {
@Override
publicvoid onScanCompleted(String path, Uri uri) {
ObservableManager.getInstance().setData(Constants.DATA_CHANGE_DELETE_SONGS);
}
});
接着从媒体库拿各个Fragment的数据,如单曲:
Stringwhere = MediaStore.Audio.Media.IS_MUSIC + "=1";
Cursor cursor = context.getContentResolver().query(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, null, where,
null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
拿到Cursor后转成你所需要的对象即可展示了:
List<MusicInfo>infos = new ArrayList<MusicInfo>();
if (cursor == null) {
return infos;
}
while (cursor.moveToNext()) {
MusicInfo info = new MusicInfo();
// 歌曲ID:MediaStore.Audio.Media._ID
long id;
if (type == Constants.TYPE_PLAYLIST) {
id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Playlists.Members.AUDIO_ID));
} else {
id = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID));
}
String title, artist;
// 歌曲文件的路径:MediaStore.Audio.Media.DATA
String url = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
String name = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DISPLAY_NAME));
// 歌曲的名称:MediaStore.Audio.Media.TITLE
title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
// 歌曲的歌手名: MediaStore.Audio.Media.ARTIST
artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
// 歌曲的总播放时长:MediaStore.Audio.Media.DURATION
long duration = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
// 歌曲文件的大小:MediaStore.Audio.Media.SIZE
long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.SIZE));
long artistsId = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST_ID));
long alb