网站首页 > 技术文章 正文
CountDownTimer是android开发常用的计时类,按照注释中的说明使用方法如下:
kotlin:
object : CountDownTimer(30000, 1000) {
override fun onTick(millisUntilFinished: Long) {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
}
override fun onFinish() {
mTextField.setText("done!")
}
}.start()
java
new CountDownTimer(30000, 1000) {
public void onTick(long millisUntilFinished) {
mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
}
public void onFinish() {
mTextField.setText("done!");
}
}.start();
上文定义了一个倒计时类,每1000毫秒执行一次,将调用onTick方法,直到3000毫秒后结束,将调用onFinish方法。
CountDownTimer在aosp开源项目中的路径为:/frameworks/base/core/java/android/os/CountDownTimer.java
接下来我们来阅读源码进行理解:
1.首先是License的说明:
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
没什么用,就不看了。
2.所属包名,也没什么用。
package android.os;
3.使用示例之前上文都说明白了,使用方法也很简单。需要注意的是后半段,意思是说,这个对象中的onTick方法是异步的, 前一次onTick没有完成前,本次的onTick是不会执行的。当onTick执行的时间超过时间间隔,就会出现没执行tick的问题。
/**
* Schedule a countdown until a time in the future, with
* regular notifications on intervals along the way.
*
* Example of showing a 30 second countdown in a text field:
*
* <div>
* <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
* <pre class="prettyprint lang-kotlin">
* object : CountDownTimer(30000, 1000) {
*
* override fun onTick(millisUntilFinished: Long) {
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
* }
*
* override fun onFinish() {
* mTextField.setText("done!")
* }
* }.start()
* </pre>
* </section><section><h3 id="java">Java</h3>
* <pre class="prettyprint lang-java">
* new CountDownTimer(30000, 1000) {
*
* public void onTick(long millisUntilFinished) {
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
* }
*
* public void onFinish() {
* mTextField.setText("done!");
* }
* }.start();
* </pre></section></div></div>
*
* The calls to {@link #onTick(long)} are synchronized to this object so that
* one call to {@link #onTick(long)} won't ever occur before the previous
* callback is complete. This is only relevant when the implementation of
* {@link #onTick(long)} takes an amount of time to execute that is significant
* compared to the countdown interval.
*/
4.接下来是CountDownTimer类的定义,首先是三个内部属性的定义:
第一个是mMillisInFuture,意思是当前时间到停止时间的毫秒数。然后是mCountdownInterval,表示间隔时间。mStopTimeInFuture虽然没说是什么,但是一看就知道是未来的停止时间。最后一个mCancelled表示这个计时器是否已经取消。
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
private long mStopTimeInFuture;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
5.接下来是构造函数,定义对象时,要提供从开始到结束的时间和ontick调用的时间间隔。比如 CountDownTimer(30000, 1000)表示整个倒计时为30秒,每10秒tick一次。
/**
* @param millisInFuture The number of millis in the future from the call
* to {@link #start()} until the countdown is done and {@link #onFinish()}
* is called.
* @param countDownInterval The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
6.结束方法,直接将 mCancelled置为ture,表示整个计时器结束,需要看到的是,将handle中的所有消息全移除了。也就是说,整个计时器是以handler为基础的。如果对handler不了解,需要先学习handler。MSG的值为1,后文有定义。
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
7.开始方法,在定义好整个计时器对象后,随时可以调用start方法,开始计时,返回值为当前的计时器对象。方法首先将mCancelled置为false,换句话说,计时器对象是可以复用的。然后判断mMillisInFuture是否小于等于0。我们可以将这个值置为负数,那样就会直接调用onFinish然后就完事了。如果mMillisInFuture正常的话,就计算未来的停止时间mStopTimeInFuture,最后让handler发送一个类型为MSG的消息。
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
8.定义了两个虚拟方法和MSG类型。onTick的参数是millisUntilFinished,表示还有多少时间计时结束。别的没什么可说的。
/**
* Callback fired on regular interval.
* @param millisUntilFinished The amount of time until finished.
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
private static final int MSG = 1;
9.最后是一个handler的定义,也是整个类最核心的部分,我们接下来详细看看。首先就直接new了一个Handler。这种方法是可能内存泄露的,不知道为什么谷歌认可了这种写法。
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
...
}
}
};
10.synchronized (CountDownTimer.this)表示处理的代码是异步的,也印证了前文说的onTick触发是异步的。如果检测到计数器取消,则直接停止处理。
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
...
}
11.首先获取剩余的时间,用停止时间减去SystemClock.elapsedRealtime(),这个方法的返回值是设备boot启动的时间。如果剩余时间没了,就直接调用onFinish()。否则继续向下走。
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else {
...
}
12.如果上一步还有剩余时间,则调用onTick,同时还要记录tick用时时间,lastTickDuration明显是onTick执行前到执行后需要的时间。
接下来定义了一个delay。我们先看最后一行sendMessageDelayed(obtainMessage(MSG), delay);说明这个delay代表下一次handlemsg的时间点。
接下来我们看if条件,millisLeft < mCountdownInterval,判断的是剩余的时间和时间间隔。一般情况下,millisLeft都是远远大于mCountdownInterval的,只有在最后一次才tick结束后,会出现true。
也就是说,如图,如果当前时间点在最后一次onTick之前,millisLeft < mCountdownInterval都是false,只有在最后一次onTick和onFinish之间的时候,才会是true。
如果当前时间点在最后一次onTick之前,当计算delay的时候,此时我们在红线的尾部,那么下一次的onTick的delay时间就是两次onTick的间隔( mCountdownInterval)减去红线的长度(lastTickDuration),最后,如果delay小于0,说明onTick执行时间过长,则不停地增加 mCountdownInterval直到delay大于0,也就是说,如果onTick执行时间过长,则放弃过期的Tick调用。我们在使用的时候需要注意,如果TIck太久,onTick执行次数可能小于预期。
如果当前时间点在最后一次onTick和onFinish之间,我们此时关注的是onFinish的时间点,delay就是剩下的时间减去红线的长度(lastTickDuration),最后还是要注意,如果Tick时间过长,则立即发送消息(delay = 0),马上执行onFinish。我觉得此时onFinish会迟到。
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
long delay;
if (millisLeft < mCountdownInterval) {
// just delay until done
delay = millisLeft - lastTickDuration;
// special case: user's onTick took more than interval to
// complete, trigger onFinish without delay
if (delay < 0) delay = 0;
} else {
delay = mCountdownInterval - lastTickDuration;
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
}
sendMessageDelayed(obtainMessage(MSG), delay);
最后,我们回顾下全文。
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.os;
/**
* Schedule a countdown until a time in the future, with
* regular notifications on intervals along the way.
*
* Example of showing a 30 second countdown in a text field:
*
* <div>
* <div class="ds-selector-tabs"><section><h3 id="kotlin">Kotlin</h3>
* <pre class="prettyprint lang-kotlin">
* object : CountDownTimer(30000, 1000) {
*
* override fun onTick(millisUntilFinished: Long) {
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000)
* }
*
* override fun onFinish() {
* mTextField.setText("done!")
* }
* }.start()
* </pre>
* </section><section><h3 id="java">Java</h3>
* <pre class="prettyprint lang-java">
* new CountDownTimer(30000, 1000) {
*
* public void onTick(long millisUntilFinished) {
* mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
* }
*
* public void onFinish() {
* mTextField.setText("done!");
* }
* }.start();
* </pre></section></div></div>
*
* The calls to {@link #onTick(long)} are synchronized to this object so that
* one call to {@link #onTick(long)} won't ever occur before the previous
* callback is complete. This is only relevant when the implementation of
* {@link #onTick(long)} takes an amount of time to execute that is significant
* compared to the countdown interval.
*/
public abstract class CountDownTimer {
/**
* Millis since epoch when alarm should stop.
*/
private final long mMillisInFuture;
/**
* The interval in millis that the user receives callbacks
*/
private final long mCountdownInterval;
private long mStopTimeInFuture;
/**
* boolean representing if the timer was cancelled
*/
private boolean mCancelled = false;
/**
* @param millisInFuture The number of millis in the future from the call
* to {@link #start()} until the countdown is done and {@link #onFinish()}
* is called.
* @param countDownInterval The interval along the way to receive
* {@link #onTick(long)} callbacks.
*/
public CountDownTimer(long millisInFuture, long countDownInterval) {
mMillisInFuture = millisInFuture;
mCountdownInterval = countDownInterval;
}
/**
* Cancel the countdown.
*/
public synchronized final void cancel() {
mCancelled = true;
mHandler.removeMessages(MSG);
}
/**
* Start the countdown.
*/
public synchronized final CountDownTimer start() {
mCancelled = false;
if (mMillisInFuture <= 0) {
onFinish();
return this;
}
mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
mHandler.sendMessage(mHandler.obtainMessage(MSG));
return this;
}
/**
* Callback fired on regular interval.
* @param millisUntilFinished The amount of time until finished.
*/
public abstract void onTick(long millisUntilFinished);
/**
* Callback fired when the time is up.
*/
public abstract void onFinish();
private static final int MSG = 1;
// handles counting down
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
synchronized (CountDownTimer.this) {
if (mCancelled) {
return;
}
final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();
if (millisLeft <= 0) {
onFinish();
} else {
long lastTickStart = SystemClock.elapsedRealtime();
onTick(millisLeft);
// take into account user's onTick taking time to execute
long lastTickDuration = SystemClock.elapsedRealtime() - lastTickStart;
long delay;
if (millisLeft < mCountdownInterval) {
// just delay until done
delay = millisLeft - lastTickDuration;
// special case: user's onTick took more than interval to
// complete, trigger onFinish without delay
if (delay < 0) delay = 0;
} else {
delay = mCountdownInterval - lastTickDuration;
// special case: user's onTick took more than interval to
// complete, skip to next interval
while (delay < 0) delay += mCountdownInterval;
}
sendMessageDelayed(obtainMessage(MSG), delay);
}
}
}
};
}
猜你喜欢
- 2024-10-29 你还在用 Date?快使用 LocalDateTime 了!
- 2024-10-29 Java修炼终极指南:79,80,81 签到终极修炼天赋
- 2024-10-29 硬核!最全的延迟任务实现方式汇总!附代码(强烈推荐)
- 2024-10-29 还在实体类中用Date?JDK8新的日期类型不香么?
- 2024-10-29 LocalDateTime 说:2020,是时候换个更好的日期时间类了
- 2024-10-29 程序员,你还在使用Date嘛?建议你使用LocalDateTime哦
- 2024-10-29 深度思考:在JDK8中,日期类型该如何使用?
- 2024-10-29 为什么建议使用你 LocalDateTime,而不是 Date?
- 2024-10-29 百度开源的分布式唯一ID生成器UidGenerator,解决了时钟回拨问题
- 2024-10-29 DeepLearning4j 实战:手写体数字识别的 GPU 实现与性能对比
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)