1. 程式人生 > >Android 仿網易雲音樂 + Java socket自建伺服器 過程&心得分享

Android 仿網易雲音樂 + Java socket自建伺服器 過程&心得分享

介紹

大二下學期的時候做的一個專案,一個線上音樂播放器
UI素材和佈局都參考了網易雲音樂,客戶端使用的是Android編寫的,而伺服器方面則是自己使用Java Socket,並自定義伺服器與客戶端之間的通訊協議~(安全方面就比較殘念) 完成的功能也比較少~ 本人也只是個小菜雞(:з)∠)

功能實現 & 實現方法

功能一:登陸,註冊

1. 要實現註冊和登陸功能,就需要一個伺服器,以及一個數據庫,用來記錄資料

2. 客戶端傳送註冊的資訊到伺服器,伺服器在資料庫中檢索是否存在此賬戶,若沒有,則將此資訊寫入資料庫中。若賬戶存在,則返回錯誤資訊給使用者,提示使用者,註冊使用者名稱已存在。並且對使用者的輸入做一些簡單的判斷,例如2次密碼的輸入必須一致。

3. 登陸則是傳送賬戶,密碼給伺服器,伺服器檢索資料庫,查詢是否存在使用者,及密碼是否正確。

4. 參照網易雲音樂的登陸機制,在使用者登陸一次後,第二次開啟應用時不需要再次登陸,直接進入主介面,關於如何判斷是否以及登陸,當登陸之後,一個靜態變數isLogin會被記錄到本地的sharePreference檔案中,每次開啟應用的時候,會去查詢這個檔案中的這個標誌變數是否為true,若為true則直接載入主介面activity,finish()登陸註冊的activity。當用戶登出時(當然這裡還沒有實現),會把isLogin設定為false,這樣下次登陸的時候就要重新進行登陸**

5. 要用到的工具有jdbc-jar和mySql。

6. 伺服器方面,自定義協議Login和Register

效果截圖如下:
1. 執行app進入登陸註冊介面。
這裡寫圖片描述
2.進入註冊介面,輸入使用者名稱和密碼
這裡寫圖片描述
3.註冊成功,再看看伺服器那邊的資訊,和資料庫
這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述

4.接下來登陸,輸入剛才註冊的賬號,並登陸,進入到主介面
這裡寫圖片描述這裡寫圖片描述這裡寫圖片描述

功能二:線上搜尋音樂

1. 自定義通訊協議Search,從客戶端傳送search “seachContent”到伺服器,然後伺服器在本地的歌曲資料庫中去查詢,是否存在該歌曲searchContent,若存在則返回查詢結果,若不存在,則提示使用者嘗試別的搜尋條件。

效果截圖如下:
1. 點選主介面右上角的搜尋按鈕,進入搜尋欄,在搜尋欄輸入要查詢的歌曲,或者歌手,或者專輯。
示例 輸入專輯3L,點選搜尋,得到結果
這裡寫圖片描述
2. 再看看伺服器方面的資訊
這裡寫圖片描述
伺服器接受到了請求,在資料庫中去尋找歌曲。(歌曲資料庫,在每次啟動資料庫的時候進行匯入)
image

功能三:線上播放音樂,內建音樂播放器

1. 線上播放音樂,就需要實現快取,別播邊放的功能。

2.伺服器接收到了download請求後,在伺服器本地找到檔案,然後把檔案傳送到客戶端,客戶端接收檔案,當接收檔案大小滿足播放要求的大小時,就啟動mediaplayer,開始播放接收到的檔案,下載和播放是多執行緒的,由於播放和下載都對檔案有讀寫操作,容易造成死鎖。這裡把正在下載的檔案備份一份給播放器用,本身那份繼續進行下載。在快播放完之前匯入的檔案時,再重新從正在下載的檔案中讀取內容,繼續播放。同時在播放的過程中,已經對播放檔案進行了快取。若要下載此歌曲,可以直接把快取檔案轉成MP3檔案。在快取完畢後,本地資料庫sqlite會記錄快取檔案的路徑以及檔案的一些資訊,待下載的時候做進一步的操作

3.播放器屬於一個隱藏的佈局,且播放的程式碼屬於多執行緒,這裡本身是想做成service的,但是由於種種原因,還是使用了多執行緒的辦法。在點選選項後,返回主介面,並使隱藏的播放器顯示出來。多執行緒的播放程式碼對progressbar和button進行了監聽和UI重新整理。

效果截圖:
1.點選搜尋的結果返回主介面進行播放,及播放完成狀態
這裡寫圖片描述這裡寫圖片描述

功能四:實現下載功能

1. 首先下載是一個比較費時的東西,所以這裡採用IntentService,一個服務,一個下載完成後就自動結束的服務,讓它來出來下載的操作

2. 下載分為2種,一種是直接從快取中copy到預設下載目錄,另一種是從伺服器上下載。

A)  在下載的時候,先檢測檔案是否存在於本地,若存在則不做任何操作,並Toast提示使用者,檔案以及存在。
B)  若檔案不存在,則先從本地資料庫中的表cacheFIle中查詢是否存在該檔案的快取檔案,如果存在,則把該檔案copy到預設下載目錄中,從表中刪除該歌曲的資訊
C)  若沒有快取,則直接向伺服器請求檔案,向伺服器傳送Download請求,伺服器返回檔案,客戶端對檔案進行讀寫,儲存在預設目錄下
D)  BC操作之後,把下載好的歌曲儲存在本地資料庫中的表dldFile中,以便之後播放本地音樂的時候使用。

效果截圖:
1. 再次進入搜尋介面輸入3l得到查詢結果,點選更多詳細資訊按鈕(選項最右邊),會得到一個popupWindow彈窗(裡面只實現了下載功能,其餘的沒有實現);點選下載
這裡寫圖片描述這裡寫圖片描述

可以看到,提示資訊已經新增到下載佇列,因為剛才已經進行了快取,所以這次下載是直接從快取目錄中去讀取檔案,轉到預設下載目錄中。
再檢索一首沒有快取的歌曲 示例ao,點選下載
這裡寫圖片描述這裡寫圖片描述
在logcat中顯示檔案來自伺服器。

2.檢視預設下載目錄資料夾,和資料庫檔案LocalDatabase
這裡寫圖片描述
這裡寫圖片描述

功能五:播放本地音樂功能

1. 在檢索函式中預先配置了預設下載目錄,所以只會檢測該目錄下的檔案。目前也只能實現檢索該目錄的功能,因為,音樂檔案的解析工作(獲取一個音樂檔案的專輯,藝術家,歌曲名字)是在伺服器進行的,當然這也可以在android上進行,但是一開始沒有注意到這點,當注意到的時候程式以及寫的七七八八了,再改就可能會耗費很多時間,所以就沒改。如果實現android上執行解析,這可能可以省事很多。

2. 檢索預設目錄中的音樂檔案,並從本地資料庫中去得到歌曲的資訊(專輯,藝術家)。並把這些資訊傳給List,再通過ListView展示得到的資訊。

3. 點選歌曲,呼叫音樂播放器類裡面的playLocalMusic方法,傳入該檔案的路徑,進行播放

效果截圖:
這裡寫圖片描述這裡寫圖片描述
點選主介面目錄裡面的本地音樂,進入本地音樂節目,可以看到顯示了剛才下載的2首歌曲。
跟著點選歌曲,返回主介面,可以看到主介面的播放器已經在播放選中的歌曲。

伺服器

  1. 首先介紹自定義的協議:Test, Login, Register, Download, Search, 後四種已經介紹過,第一個是用來測試網路是否通暢的,若無法連同網路,則提示使用者當前網路不可用。

  2. 伺服器自定義了一些錯誤程式碼:000,001,002,003; 100,101,102.等等。也可以不用理會,客戶端和伺服器端彼此都知道的東西。

  3. 伺服器連同了MySql資料庫,使用Sql語句就能夠對資料進行處理,查詢歌曲的資訊,已經賬戶資訊,返回給使用者。
  4. 伺服器實現了對音訊檔案的解析,呼叫第三方jar包 JAudioTager,可以對音樂檔案裡面的資訊進行解析,從而得到專輯,藝術家,時長,等資訊,返回給客戶端,讓客戶端播放器能夠正確的顯示。

後面分享一下個人的所想所得~

一.伺服器

  1. 首先要實現一個app,就需要自己搭建一個伺服器,直接用serversocket來做伺服器,也不考慮安全性什麼的了,先實現以後再說,所以客戶端和伺服器進行的是socket通訊。這些是小兒科,但是用java連線MySQL資料庫是個問題,對於一個對資料庫一無所知的小白來說,可是花了2天的時間去搞這個資料庫,安裝弄了半天,看SQL語法用了1天左右,才差不多搞懂了。接下來的1天就是學習如何使用MySQL提供的java開發包來和MySQl進行連線通訊。翻閱了一些資料 jdbc 知道了它的用法,其實也蠻簡單的。
  2. 連線伺服器是弄好了,可是怎麼把歌曲解析呢?看到的網易雲音樂都是有專輯,藝術家的,這些要如何獲取。百科一下了解到了音訊檔案是有一些特殊的地方的,音訊檔案講解 ,每個mp3裡面都有一些標籤id3,
    第一代是idv1(version 1),基本已經無人使用,現在大多數都是idv2,idv2又分了一些版本2.1 – 2.4。這些也不用去管。藉助第三方包 JAduioTagger,呼叫其中的方法,就可以獲得這些檔案的資訊了。再通過jdbc,就實現了把音樂檔案的資訊全部匯入到資料庫中,這樣就解決的檔案提供的問題。

二.客戶端(Android)

先從簡單的登陸和註冊介面開始,用到的都是簡單的UI,沒什麼好說。除了記住密碼的功能比較有技巧,記錄密碼就是根據checkBox的狀態,判定是否把賬戶密碼寫到本地的sharePreference檔案中,再根據裡面記錄的內容,判定應用開啟的時候是否裝載進來。還有的是,登陸和註冊都涉及到了socket程式設計,Android裡面是不允許在主執行緒中去執行網路連線的,所以要用到多執行緒。

然後是主介面,主介面要用到ListView,關於ListView就不得不吐槽了,這是一個用得最多,也是最難的一個UI,在做這個APP的時候,經常用到,吃了不少苦。使用ListView首先要有一個context,用於承載ListView的內容,還有一個ListView裡面每個選項的佈局,還有一個List陣列,裝了選項佈局裡面需要的內容。仿照雲音樂的介面,做出類一個listView,不過下面的收藏歌單,實在是沒有精力和能力去實現,所以也沒多加考慮。先為本地音樂,建立一個activity,稍後再去實現它。目前優先解決搜尋功能。實現標題導航,要用到actionbar,一個在平時用的很多的標題欄。建立一個搜尋按鈕searchView,為其註冊監聽器,當輸入內容的時候啟用另一個活動 SearchActivity顯示查詢的結果。

上面的那些都還是比較簡單的,不過ListView以及佈局等等問題,在做專案的過程中確實佔用了很多的時間。搜尋做完了,就輪到播放了,一個最為頭疼了問題,在這個播放問題上,耗費了將近1個星期的時間,真的很不容易。
播放要怎麼播放?翻了翻書,哦,用MediaPlayer,這只是簡單的提供了一些方法和介紹了它的生命週期而已,並沒有給予我什麼啟發。
首先要實現線上播放,線上播放究竟是個什麼原理,起初是想直接把它下下來,然後播放,敷衍一下就算了,但是一想到,你點一首歌,使用者要等半天才放,這是不是不太合理,於是就在茫茫程式碼中去尋找辦法,網上的線上播放,很多都是直接丟一個uri然後通過呼叫MediaPlayer自帶的serDataSourece方法進行播放,毫無技術性可言。不過終於讓我發現了一份差不多符合自己想法的資料,自己好像發現了些什麼。首先線上播放其實也是下載,不過它是下一點,播放一點,思路大概如下:

image

downloadingFile為正在下載的檔案,playingFile為正在播放的檔案,bufferFile為快取檔案
downloadCom為下載完成。播放要求為:檔案大小>512k
getCurPos()為獲取當前播放進度,getDuration()為當前MP3時長 –>為複製操作

看得出來,這裡涉及到訪問網路操作,所以把它丟到多執行緒中,但是播放要更新progressbar進度條,這又迎來了一個難題,再查資料,在子執行緒更新UI要用到非同步機制,使用handler就可以解決這個問題。其實,這個播放條本該顯示在所有活動之上的,但是當初決定了用activity,要改成fragment就要全改,也試過全改了,但是還是失敗了,換成碎片以後標題欄又成了一個問題,之前在fragment和activity之間折騰了2-3天,還是改回activity,改回activity後又想著讓播放條作為一個baseactivity讓所有activity繼承它,但是這種辦法又遇到各種問題,又改了回來。跟著無意間看到Service,本身音樂播放應該是一個Service才對了,這才是一個正確的方向!!於是又接著去嘗試Service,Service可以與很多活動通訊,而Service始終還是一個,這就很好的解決了一個activity和另一個activity之間執行緒的管理問題。但是神奇的事情發生了,在Service中更新UI失敗了,不過按照網上的說法應該是可以的,不過時間已經不允許我去糾結了,所以還是打回原形。到處碰壁那種辛酸真的很難受。最終發現了一個很強的東西,廣播!!廣播機制實現了非activity類和activity的通訊,只需要一個廣播就可以呼叫activity裡面的方法, 例如更新UI!!可惜已經到了deadline了,也由於這個專案,落下了很多東西,半個月來幾乎每天都在做這個專案,真的不容易。不過對Android的瞭解更近一層了。因為親身實踐了,親身去跳坑,爬坑,雖然滿身傷痕,不過每當實現一個功能的時候,心裡是欣喜不已的,還興沖沖的分享給身邊的舍友。這種喜悅感是難以言喻的。
在做專案的時候,有查bug的痛苦,也有取得成就的喜悅,悲歡參半,收穫頗豐。不過還是有很多的遺憾,如果當初知道fragment,當初知道Service,當初知道broadcast
沒有人指導跳進了很多坑,不過跌跌撞撞讓我明白,有很多東西不親身去經歷是不知道那是多麼的難。我們平時用的那麼順手的app開發起來是那麼的難。如果有下次,我可能會做得更好。

最後放上原始碼地址~