1. 程式人生 > >Android Studio:服務與多執行緒--簡單音樂播放器

Android Studio:服務與多執行緒--簡單音樂播放器

一、 實驗題目 

 服務與多執行緒--簡單音樂播放器

【目的】

1. 學會使用 MediaPlayer;

2. 學會簡單的多執行緒程式設計,使用 Handle 更新 UI;

3. 學會使用 Service 進行後臺工作;

4. 學會使用 Service 與 Activity 進行通訊。

二、 實現內容

實現一個簡單的播放器,要求功能有:

1. 播放、暫停,停止,退出功能;

2. 後臺播放功能;

3. 進度條顯示播放進度、拖動進度條改變進度功能;

4. 播放時圖片旋轉,顯示當前播放時間功能;

三、 實驗過程

我的MainActivity.java主要呼叫函式過程如下,我將按以下順序逐步寫實驗過程。


 1. UI介面:

 

我的UI介面如上所示。在完成實驗文件提供的主介面佈局後,自己進行了一些修改:

①在res/values/styles中去掉應用標題欄,將頂部狀態列底色改為黑色。

②更換Manifest中的app圖示icon。同時為主佈局增加一張背景圖。

③為button新建樣式shape.xml,通過background=”@drawable/shape”引入。

④改變seekbar的原有樣式,包括thumb顏色和進度條高度。

 2. SD卡動態檔案讀取許可權申請:

由於我是在Android 7.1.1的手機上進行測試執行,所以我在將mp.3檔案放入指定路徑後,必須先設定動態獲取檔案許可權,在徵得使用者許可權認可後才可讀取音樂檔案。

首先在Manifest裡註冊許可權:

 

然後在MainActivity.java裡新增許可權確認函式,請求許可權會彈出詢問框:


再增加如下的回撥函式,在使用者進行選擇後會呼叫該函式,若使用者拒絕則直接退出程式:

 

執行測試效果如下:

 

 3. Service+MediaPlayer

先是建立一個service類,命名為MusicService.java。並且在Manifest裡進行註冊。

然後通過Binder來建立MainActivity.java與MusicService.java的通訊。

在MainActivity.java裡通過如下程式碼,使得activity啟動時便通過bindService,從而與MusicService繫結。bindService成功後回撥onServiceConnected函式,通過IBinder 獲取Service物件。

 

同時在MainActivity.java裡重寫onDestroy函式,當程式結束後,service停止服務,此時必須解除繫結。

 

關於service繫結,MainActivity.Java部分講完,接下來講MusicService.java裡的設定。

既然已經activity已經繫結service,那麼我們可以將媒體檔案放在service裡,然後通過binder來保持通訊。(以下程式碼皆寫在MusicService.java)

 

首先在service裡讀取sd卡里指定路徑的mp3檔案:

 

然後通過binder裡的onTransact函式來保持通訊。

 

上面的onTransact函式是service能夠響應activity傳送過來的呼叫請求。code是訊號,data是指activity傳送過來的資料,reply是指service傳回給activity裡的資料。

data和reply可以通過write和read寫入/讀取傳送的值。

而在MainActivity.Java裡,我們通過transact向service傳送請求保持通訊,通過這一機制我們便可以控制service裡mp3檔案的播放。以下是部分button的click事件。

 

(QUIT退出鍵,直接結束程式。)

至此,基本可以實現button對音樂播放的控制。

 4. Handler

接下來只剩進度條的部分還沒完成,由於我們要時刻捕獲音樂的播放進度從而改變音樂的當前時間和seekbar上thumb的位置,所以需要採取多執行緒thread,再通過handler來實時更新UI。我們先定義一下時間的顯示格式:

 

然後定義Thread和Handler,如下,實現音樂的當前時間和seekbar的實時更新。

 

注意到上面程式碼裡,要獲取音樂檔案的播放進度時(getCurrentPosition),本來是想通過transact進行傳值回撥的,但是發現,直接引用MusicService裡的mediaplayer也可以實現而且方便了很多,這麼說,之前的button控制播放也可以引用MusicService.mp.start()之類的來實現,這樣豈不是減少了程式碼量?不知道transact和“直接引用”這兩者有優劣之分嗎?求解。

接著實現拖動進度條改變音樂播放進度的函式:

 

 5. 補充一下ObjectAnimator

忘了提圖片旋轉的動畫實現了,這裡補充下。

 

 

然後通過 .start() / .pause() / .resumn()來控制圖片的旋轉與否。

之前我不知道.setDuration()即是通過設定動畫時長從而來控制動畫變化的速率,一開始裡面填的是1000,結果圖片轉得像螺旋一樣飛快,網上查了很久居然找不到設定速率的程式碼真是惱人,後來還是靠自己突然開竅發現的……

四、 實驗思考及感想

(1)這次實驗其實挺喜歡的。主要是因為之前幾周實驗都對著那個藍白的商品列表不斷地新增功能,說實話有點審美疲勞了。新專案才能喚起新鮮感。

(2)說幾個實驗遇到的小錯誤:

①剛開始點選stop按鈕後停止音樂播放,但是再次點選PLAY的時候音樂卻無法重新開始,究其原因,原來是mediaplayer設定了.stop()之後應該再順便設定.prepare()讓音樂進入就緒狀態的。不過直接在.stop()後面設定了.prepare()又報錯了,只得通過try catch才能成功設定。不知道是為什麼。

 

②設private IBinder mBinder時候居然漏了IBinder的首字母I,然後還設定成功了。結果下面onServiceConnected函式倒是一堆報錯。找了好久才發現這個bug…… 

③這兩處地方一直顯示紅波浪線,解決不了。但是程式碼能正常執行……

 


④實驗過程中遇到很多各式各樣的小bug,然而寫報告的時候又想不起多少了。