13

强迫症的研究——MediaPlayer播放进度条的优化

 3 years ago
source link: https://blog.csdn.net/eclipsexys/article/details/49820177
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

强迫症的研究——MediaPlayer播放进度条的优化

如何做一个优美、流畅而且准确的播放进度条,也许很多人觉得很简单,但实际上,这个问题在大部分时间都被忽略了。

计时方式的比较

  • 计时方式——主线程中使用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. 解耦、装逼

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK