Android 音樂APP核心功能的實現(xiàn)(掃描本地、Service播放、循環(huán)模式、進(jìn)度更新、列表增刪等)

一、前言

? ? ? 自己做的音樂APP的界面部分已完成,各種功能基本實現(xiàn)了。但是我對音樂后臺播放這一塊(Service)不太滿意。因為之前對Service接觸得不多,所以沒有一個明確的規(guī)劃,想要實現(xiàn)什么就往Service里添加新的需要實現(xiàn)的功能,導(dǎo)致原來的Service的代碼十分臃腫和凌亂。

? ? ? 一款音樂APP,最核心的業(yè)務(wù)就是播放音樂,所以設(shè)計好一個完善的音樂APP的Service十分有必要。自己在動手寫的時候參考了網(wǎng)上許多的例子,Github里完整的項目,由于編碼風(fēng)格和自己的不太一樣,理解起來效率很低,一些博客里發(fā)布的僅有單個功能的源碼又太簡單。因此我整理了下自己的思路,做一次總結(jié),以獲得更好的提升。

二、實現(xiàn)功能

? ? ? 這個MusicService主要實現(xiàn)了如下功能:

基本功能

? ? ? 播放本地歌曲、網(wǎng)絡(luò)歌曲,實現(xiàn)播放、暫停、下一首、選擇循環(huán)模式、拖動進(jìn)度條改變播放位置等基本功能。

讀取SQLIte數(shù)據(jù)

? ? ? 1、啟動應(yīng)用后,能夠獲取前一次未播放完的音樂信息(如當(dāng)前時間、歌曲總長、進(jìn)度條位置、歌曲、歌手、循環(huán)模式等),并裝入下端播放欄中,點“播放”在暫停處續(xù)播;

? ? ? 2、進(jìn)入本地音樂界面時,如之前已經(jīng)啟動過引用并掃描過本地音樂,則直接載入歌曲信息,如還沒有掃描可點擊界面的右上角彈出Dialog選擇“掃描本地音樂”選項進(jìn)行掃描;

? ? ? 3、在MusicService通過SQLite獲得歌曲信息。

用戶體驗

? ? ? 1、點擊播放欄“列表”圖標(biāo),通過DrawerLayout的方式彈出播放列表;

? ? ? 2、點擊本地音樂列表或者網(wǎng)絡(luò)歌單的歌曲即可播放,并且將當(dāng)前播放的音樂自動添加至播放列表,當(dāng)用戶改變循環(huán)模式,或者增刪本地列表、播放列表的歌曲時,MusicService均可正確地播放下一首歌曲;

? ? ? 3、通過廣播發(fā)送通知,讓Activity更新UI。

三、效果圖

? ? ? 效果圖如下所示:

4、關(guān)鍵思路

本Demo的Java文件包括:

? ? ? 后臺Service:

? ? ? MusicService,用于在后臺播放音樂以及進(jìn)行各種操作的服務(wù)類

? ? ? 前臺Activity

? ? ? Activity:MainActivity:用于進(jìn)入本地音樂界面和網(wǎng)絡(luò)音樂界面的活動類

? ? ? LocalMusicActivity:顯示本地音樂列表界面的活動類

? ? ? OnlineMusicActivity:實現(xiàn)網(wǎng)絡(luò)歌曲(也就是我APP中歌單)界面的活動類

? ? ? 還有一些工具類、線程類分別用于掃描本地音樂,適配ListView等延時操作,詳見源碼。

? ? ? 此外,還需要用到SQLite去對數(shù)據(jù)進(jìn)行存儲和提取,我建立了一個名為music.db的數(shù)據(jù)庫,并在這個數(shù)據(jù)庫里建立了playerbarinfotb、localmusictb、onlinemusictb、playlisttb、loadlocalmusiclistviewtb這幾個表格,分別管理播放欄數(shù)據(jù)、本地音樂、網(wǎng)絡(luò)音樂、播放列表音樂、是否曾掃描并載入本地音樂等信息。

? ? ? 還需要在Activity里定義localsong_list、onlinesong_list和play_list分別存儲本地音樂、網(wǎng)絡(luò)歌曲以及播放列表的歌曲信息。

代碼運行流程就是:

·初始化

? ? ??啟動Activity時先對數(shù)據(jù)庫里的playerbarinfotb這一個表格中去搜索,如果存在數(shù)據(jù),就將播放欄各項信息提取出來,初始化播放欄。

·進(jìn)入本地

? ? ? 進(jìn)入本地音樂后,搜索loadlocalmusiclistviewtb,如果存在數(shù)據(jù),說明已經(jīng)掃描過本地音樂,因此從localmusictb里將全部的歌曲信息提取出來,載入本地音樂界面的ListView中,如果loadlocalmusiclistviewtb不存在數(shù)據(jù),就顯示“掃描本地”按鈕,對手機上的本地音樂進(jìn)行掃描,并在掃描完成后將掃描結(jié)果載入ListView。

? ? ? 在MusicService里,用public static final String的語句定義好各種動作,如:

? ? ? public?static?final?String?ACTION_UPDATE_TIME="ACTION_UPDATE_TIME";

? ? ? public?static?final?String?ACTION_PLAY_CURR_MUSIC="ACTION_PLAY_CURR_MUSIC";

? ? ? public?static?final?String?ACTION_PLAY_ONLINE_MUSIC="ACTION_PLAY_ONLINE_MUSIC";

? ? ? public?static?final?String?ACTION_PLAY_LOCAL_MUSIC="ACTION_PLAY_LOCAL_MUSIC";

? ? ? public?static?final?String?ACTION_PLAY_PLAYLIST_MUSIC="ACTION_PLAY_PLAYLIST_MUSIC";

? ? ? public?static?final?String?ACTION_DELETE_LOCALMUSIC="ACTION_DELET_LOCALMUSIC";

? ? ? public?static?final?String?ACTION_DELETE_PLAYLIST_MUSIC="ACTION_DELETE_PLAYLIST_MUSIC";

? ? ? public?static?final?String?ACTION_PLAY_NEXT="ACTION_PLAY_NEXT";

? ? ? public?static?final?String?ACTION_PLAY_ALL_LOCALMUSIC="ACTION_PLAY_ALL_LOCALMUSIC";

? ? ? public?static?final?String?ACTION_PLAY_ALL_ONLINEMUSIC="ACTION_PLAY_ALL_ONLINEMUSIC";

? ? ? public?static?final?String?ACTION_CLEAR_ALL_PLAYLIST="ACTION_CLEAR_ALL_PLAYLIST";

? ? ? public?static?final?String?ACTION_CHANGE_CIRCULATE_MODE="ACTION_CHANGE_CIRCULATE_MODE";

? ? ? 這些動作顧名思義,分別是“更新時間”、“播放當(dāng)前播放欄音樂”、“播放網(wǎng)絡(luò)音樂”、“播放本地音樂”、“播放音樂列表里的音樂”、“刪除本地歌曲”、“刪除播放列表里的歌曲”、“播放下一首”、“播放所有本地音樂”、“播放所有網(wǎng)絡(luò)音樂”、“清空播放列表”等動作。

·播放歌曲

? ? ? 如果點擊了本地音樂列表里的某一首歌,就通過

? ? ? intent.setAction(MusicService.ACTION_PLAY_LOCAL_MUSIC);

? ? ? 的方法,向intent添加了代表了“播放本地音樂”的這個動作標(biāo)示,與此同時還通過

? ? ? Intetn.putExtra(“position”,position);

? ? ? 的方法傳入當(dāng)前點擊的歌曲在本地歌曲列表中的位置,接著用

? ? ? startService(intent);

? ? ? 的方法啟動項目的服務(wù)類MusicService。MusicService在onStartCommand()方法里通過

? ? ? if(intent.setAction.equals(MusicService.ACTION_PLAY_LOCAL_MUSIC);

? ? ? 的語句去判斷傳入的動作標(biāo)識是什么,并執(zhí)行相應(yīng)的操作。例如播放列表里有歌曲A、B、C、D、E順序排列,他們的播放路徑分別是“sdcard/a.mp3”、“sdcard/b.mp3”、“sdcard/c.mp3”、“sdcard/d.mp3”、“sdcard/e.mp3”,用戶點擊的是本地音樂列表里的第二首歌,MusicService就開始獲取傳入的值position,為1(從0開始算起),在MusicService里提取本地音樂的數(shù)據(jù)庫,通過position找到用戶點擊的是歌曲B。然后播放這首歌。

·添加

? ? ? 歌曲播放的同時,程序會對播放列表的數(shù)據(jù)庫playlisttb進(jìn)行搜索,如果playlisttb里面沒有當(dāng)前播放的歌曲,那么用insert語句將其添加至playlisttb,用.add()語句添加至play_list,并通過Adapter的.updateData()方法刷新播放列表的ListView界面。

? ? ? 歌曲播放時通過定時線程,每秒發(fā)送一次播放器信息廣播,當(dāng)前Activity接收到廣播后更新播放欄的信息。

·下一首

? ? ? 歌曲開始播放之后,在MusicService里執(zhí)行g(shù)etNextSong()的方法,根據(jù)循環(huán)模式,獲取下一首播放的歌曲。列表循環(huán)的時候,獲取歌曲B的下一首也即歌曲C,單曲循環(huán)的時候,獲取夏一首歌曲也即歌曲B,隨機播放的時候,通過Ramdom取出一個范圍為0到(play_list.size()-1)的隨機數(shù)m,用play_list.get(m)的方法獲取下一首待播歌曲。當(dāng)監(jiān)聽到當(dāng)前歌曲播放完畢后,執(zhí)行播放下一首。

·刪除

? ? ? 當(dāng)刪除本地音樂或者播放列表里的歌曲C時,也獲取音樂的position,為2,并發(fā)送刪除歌曲的動作標(biāo)示到MusicService,MusicService根據(jù)position判斷刪除的是歌曲C,如果當(dāng)前循環(huán)模式是列表循環(huán),刪中的正好是下一首待播的歌曲,那么就通過歌曲的路徑,獲取另外的正確的下一首待播歌曲,得到歌曲D(接著D又從播放列表中被刪除,getNextSong()又獲取歌曲E)

·循環(huán)

? ? ? 在MusicService里定義一個公共的int變量MUSIC_CIRCULATION_MODE,設(shè)置終態(tài)變量來表示循環(huán)模式,如public? static final int LIST_CIRCULATION=0表示列表循環(huán),public? static final int SINGLE_CIRCULATION=1表示單曲循環(huán),public? static final int SHAFFULE=2表示隨機播放。點擊播放欄的循環(huán)圖標(biāo)可以改變MusicService的公共變量MUSIC_CIRCULATION_MODE,點擊一次就輪次賦值列表循環(huán)、單曲循環(huán)、隨機播放這三個常量,并通過動作標(biāo)示啟動MusicService,MusicService根據(jù)switch(MUSIC_CIRCULATION_MODE),啟動getNextSong()方法去獲取下一首待播歌曲。

·網(wǎng)絡(luò)歌單

? ? ? 網(wǎng)絡(luò)歌單的頁面在我自己的APP里是通過一個HttpThread的線程類去獲取我服務(wù)端上的歌單列表的,在這個源碼里我就直接定義一個list并直接往里面添加歌曲信息了。只是歌曲的URL是我家局域網(wǎng)里服務(wù)端的歌曲地址,如有需要可以直接改成別的網(wǎng)絡(luò)歌曲連接。

·MainActivity

? ? ? 在這個源碼中我并沒有設(shè)置從本地音樂界面或網(wǎng)絡(luò)歌單界面跳轉(zhuǎn)回主界面的方法,返回主界面上通過菜單上的返回鍵進(jìn)行的,因此,為解決本地音樂界面或者網(wǎng)絡(luò)歌單界面返回主界面后,主界面由于沒有重新onCreate()而沒有更新播放列表的問題,我重寫了onResume()方法,加入了一個刷新播放列表的ListView的方法。使得幾個界面的播放列表能保持同步。


5、源碼

? ? ??安卓音樂APP后臺Service源碼

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容