MediaPlayer播放進度條的優化
原文出處:http://www.jianshu.com/p/56f37988428b
如何做一個優美、流暢而且準確的播放進度條,也許很多人覺得很簡單,但實際上,這個問題在大部分時間都被忽略了。
計時方式的比較
-
計時方式――主線程中使用Handler
-- 這種方式最簡單,在主線程中通過handler.postDealyed(……, 1000),并在onHandleMessage中繼續post消息,這樣就實現了每隔1000ms進行一次消息循環。
-
計時方式――使用單獨計時線程
-- 單獨創建一個計時線程,每秒發出time tick事件,主線程通過該事件來更新進度。這種方式比較麻煩,但是不麻煩怎么裝逼呢?
如何高雅、準確的實現
對于Handler方式
-
自身誤差
這種方式下,如果使用handler.postDealyed(……, 1000)方式來進行每秒的計時,是不準確的,是的,有很大誤差,誤差的原因在于在你收到消息,到你重新發出handler.postDealyed的時間,并不是瞬間完成的,這里面有很多邏輯處理的時間,即使沒有邏輯處理的時間,handler本身也是耗損性能的,所以消息并不可能按照理想的1000延遲來進行發送,這就導致了誤差的累積。
-
線程調度誤差
我們知道,當音樂線程啟動,到handler發出消息,這一段時間內,存在進程調度或者其它邏輯的耗時操作,導致這兩個時間并不是同時發生的。所以,我們每次在post的時候,都需要對計時進行下補償,但是,怎么做呢?
對于Handler方式的優化
我們知道,Android中有很多計時的控件,首先想到的是DigitalClock,結果發現已經廢棄,好吧,看被什么替換了,OK,發現了TextClock,代碼多了不少,感覺更牛逼了。我們直接看他是怎么處理這個問題的:
同樣是通過程序員的嗅覺找到這里:
private final Runnable mTicker = new Runnable() { public void run() { onTimeChanged(); long now = SystemClock.uptimeMillis(); long next = now + (1000 - now % 1000); getHandler().postAtTime(mTicker, next); } };
哎呦,有點意思,我們之前是通過postDelay來觸發消息事件的,但這里系統使用了postAtTime,這是為什么呢?很自然我們會想到前面兩行代碼,其實也不用想太多,你代個值進去試下就知道了,假如now取出來是1200,那么next = 1200 + (1000 - 1200 % 1000)也就是next= 2000。你看,雖然我們前一次本該在1000觸發的事件,被各種邏輯延遲到1200,那么如果你用postDelay,這個延遲就被累積了,但如果用這種方式,誤差就被補償了。
我們就叫他誤差補償算法吧~
對于單獨計時線程方式
對于單獨計時的線程,由于時間點的觸發事件和主線程已經分開了,計時線程就不會受主線程邏輯的阻塞了,所以,只要保證開始時對起始時間差進行下同步就OK了。
對于單獨計時線程方式的優化
其實對于單獨計時線程來說,已經沒有什么好優化的了,而且優點還能再列舉不少:
-
計時邏輯與UI邏輯分離,方便拓展
-
計時準確,可以將計時線程封裝,暴露接口,方便拓展
-
解耦、裝逼
如果你還要再進一步優化的話,可以在計時的時候,使用時間差的方式來統計,雖然沒什么亂用。