MediaPlayer播放进度条的优化

jopen 8年前

原文出处: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了。

对于单独计时线程方式的优化

其实对于单独计时线程来说,已经没有什么好优化的了,而且优点还能再列举不少:

  1. 计时逻辑与UI逻辑分离,方便拓展

  2. 计时准确,可以将计时线程封装,暴露接口,方便拓展

  3. 解耦、装逼

如果你还要再进一步优化的话,可以在计时的时候,使用时间差的方式来统计,虽然没什么乱用。