android倒计时代码 精确度,Android 关于 CountDownTimer onTick() 倒计时不准确问题源码分析...

第一次写源码分析类博客,如有错误,欢迎讨论和指正~~ (^_^)

--------------------------------------

最近在写一个倒计时控件 CountdownView , 发现系统自带的 CountDownTimer onTick() 并不准确,当然,它的倒计时长度还是比较准确的。

本博客 demo 见: countdown

一、问题

CountDownTimer 使用比较简单,设置 5 秒的倒计时,间隔为 1 秒。

final String TAG = "CountDownTimer";

new CountDownTimer(5 * 1000, 1000) {

@Override

public void onTick(long millisUntilFinished) {

Log.i(TAG, "onTick → millisUntilFinished = " + millisUntilFinished + ", seconds = " + millisUntilFinished / 1000);

}

@Override

public void onFinish() {

Log.i(TAG, "onFinish");

}

}.start();

以 API 25 为例。即 app 的 build.gradle 中设置的编译版本是 25(后续会提到版本问题)。

compileSdkVersion 25

我们期待的效果是:“5-4-3-2-1-finish”或者“5-4-3-2-1-0”。这里,我认为 显示 0 和 finish 的时间应该是一致的,所以把 0 放在 onFinish() 里显示也可以。

先看一下运行效果图:

(demo 的 log 前面的毫秒数是手机当前系统时间戳)

20812f43f08ced7ba1ca2561d3105ec1.gif

打印日志可以看到有几个问题:

问题1. 每次 onTick() 都会有几毫秒的误差,并不是期待的准确的 "5000, 4000, 3000, 2000, 1000, 0"。

问题2. 多运行几次,就会发现这几毫秒的误差,导致了计算得出的剩余秒数并不准确,如果你的倒计时需要显示剩余秒数,就会发生 秒数跳跃/缺失 的情况(比如一开始从“4”开始显示——缺少“5”,或者直接从“5”跳到了“3”——缺少“4”)。

问题3. 最后一次 onTick() 到 onFinish() 的间隔通常超过了 1 秒,差不多是 2 秒左右。如果你的倒计时在显示秒数,就能很明显的感觉到最后 1 秒停顿的时间很长。

bd418ff6d4e53ca225e29813bcc99197.png

仔细看一下日志里标注的地方,如果你想直接看解决方案,可以直接滑到日志最下方,或者在顶部目录里选择最后一栏“三、终极解决”查看。

二、分析源码

(一)API 25 源码分析

查看 CountDownTimer 源码(API 25),

a4290e98e4bd9df66364c18b4895d67f.png

发现 start() 中计算的 mStopTimeInFuture(未来停止倒计时的时刻,即倒计时结束时间) 加了一个 SystemClock.elapsedRealtime() ,系统自开机以来(包括睡眠时间)的毫秒数,后文中以“系统时间戳”简称。

即倒计时结束时间为“当前系统时间戳 + 你设置的倒计时时长 mMillisInFuture”,也就是计算出的相对于手机系统开机以来的一个时间。

015d784b97ce9c7addf7d70f7450b199.png

继续往下看,多处用到了 SystemClock.elapsedRealtime() 。

4bd26a108eff8341c1a3e74e1cc32703.png

在源码里添加 Log 打印看看。(直接在源码里修改是不会打印出来的,因为运行时不是编译的你刚刚修改的源码,而是手机里对应的源码。我复制了一份源码添加的 Log,见 demo 里的CountDownTimerCopyFromAPI25.java)

String TAG = "CountDownTimer-25";/**

* Start the countdown.

*/

public synchronized final CountDownTimerCopyFromAPI25 start() {

mCancelled = false;

if (mMillisInFuture <= 0) {

onFinish();

return this;

}

//Add

Log.i(TAG, "start → mMillisInFuture = " + mMillisInFuture + ", seconds = " + mMillisInFuture / 1000 );

mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;

//Add

Log.i(TAG, "start → elapsedRealtime = " + SystemClock.elapsedRealtime());

Log.i(TAG, "start → mStopTimeInFuture = " + mStopTimeInFuture);

mHandler.sendMessage(mHandler.obtainMessage(MSG));

return this;

}// handles counting down

@SuppressLint("HandlerLeak")

private Handler mHandler = new Handler() {

@Override

public void handleMessage(Message msg) {

synchronized (CountDownTimerCopyFromAPI25.this) {

if (mCancelled) {

return;

}

final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

//Add

Log.i(TAG, "handleMessage → elapsedRealtime = " + SystemClock.elapsedRealtime());

Log.i(TAG, "handleMessage → millisLeft = " + millisLeft

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值