MediaPlayer中onCompletion和onError的坑





問題:

在做音樂播放功能時,發現點擊下一曲或者上一曲有時會跳過曲目播放,通過檢查下一曲next()和上一曲pre()方法邏輯並沒有發現什麼問題,爲什麼會出現這種情況呢?

基本功能:

1.可以手動點擊上一曲下一曲
2.設置OnCompletionListener當播放完成時自動播放下一曲
3.播放時通過Handler循環獲取當前播放進度和總時長更新視圖

解決過程:

分析:通過打印日誌發現,onCompletion方法在歌曲未播放完成時被調用了!很奇怪的問題,常識上來講,只有音樂播放完成時此方法纔會被調用。百思不得其解的情況下,忽然想到代碼中只設置了OnPreparedListener和OnCompletionListener會不會是播放過程中發生了什麼錯誤?於是設置OnErrorListener打印日誌同時當錯誤回調時也執行下一曲:
這裏寫圖片描述

這裏寫圖片描述

嗯哼?position:6位置的確發生了錯誤直接跳到了7位置準備播放。but,剛剛提到的問題是onCompletion被調用了,怎麼添加了OnErrorListener後onCompletion不再回調,只回調了onError?

不理解,好吧,翻一下源碼。於是,MediaPlayer的Handler處理消息有這麼一段代碼
這裏寫圖片描述

也就是說當有設置MediaPlayer的OnErrorListener時就在調用onError方法,error沒有被處理,即沒有設置錯誤監聽或者onError方法返回false(通過log驗證結論),就調用OnCompletionListener的onCompletion方法。

好吧,道理我都懂,但是哪裏產生的錯誤呢?回到onError中打印日誌時順帶把錯誤代碼打印了一下發現錯誤代碼-38,搜索了一下網上的答案大概有如下結論:

1) 當一個MediaPlayer對象被新建或調用reset()方法之後,它處於空閒狀態,在調用release方法之後,纔會處於結束狀態。
2) 一個新建的MediaPlayer對象在調用getCurrenProgress()、getDuration、
getVideoHeight()、getVideoWith()、setAudioStreamType(int)、
setLooping(boolean)、setVolume(float,float)、pause()、start()、stop()、
seekTo()、prepare()、prepareAsync()方法時,不會觸發OnErrorListenerError()事件,但是
MediaPlayer對象如果調用了reset()方法後,再使用這些方法則會觸發OnErrorListenerError()事件。

以上結論不一定都準確,但給了一個參考方向。
過濾了一遍代碼,又根據log中error出現位置的不確定性,終於確定是因爲handler中循環調用getDuration,當點擊下一曲後MediaPlayer已釋放資源,下一曲資源還沒有準備完成時,剛好handleMessage中調用了getDuration就會發生錯誤。於是在點擊時先移除了相應的消息,完美解決了問題!

總之,在onCompletion中完成相應的業務邏輯時一定要謹慎,最好要添加OnErrorListener監聽錯誤信息並返回true。




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章