一个轻量级的Android计时器库,提供 AndroidTimer、CountDownTimer、AndroidTick 三个相互独立的核心组件,前两者支持灵活的状态管理。
在模块目录的 build.gradle 文件中添加:
dependencies { implementation 'io.github.xesam:android-timer:0.4.3' }
dependencies { implementation 'io.github.xesam:android-timer-kt:0.4.3' }
android-timer-kt已通过api依赖传递android-timer与kotlinx-coroutines,无需重复声明。
// 创建计时器:每1000ms触发一次onTick AndroidTimer timer = new AndroidTimer(1000L) { @Override protected void onTick(long millisFly) { Log.d("Timer", "Elapsed: " + millisFly + "ms"); } @Override protected void onStart(long millisFly) { Log.d("Timer", "Started at: " + millisFly + "ms"); } @Override protected void onPause(long millisFly) { Log.d("Timer", "Paused at: " + millisFly + "ms"); } @Override protected void onResume(long millisFly) { Log.d("Timer", "Resumed from: " + millisFly + "ms"); } @Override protected void onCancel(long millisFly) { Log.d("Timer", "Cancelled at: " + millisFly + "ms"); } }; // 启动 timer.start(); // 暂停 timer.pause(); // 恢复 timer.resume(); // 取消 timer.cancel();
// 创建倒计时器:10秒倒计时,每100ms更新一次 CountDownTimer countDownTimer = new CountDownTimer(10000L, 100L) { @Override protected void onStart(long millisUntilFinished) { // 倒计时开始 Log.d("CountDown", "Started: " + millisUntilFinished + "ms remaining"); } @Override protected void onTick(long millisUntilFinished) { // 倒计时进行中 Log.d("CountDown", "Remaining: " + millisUntilFinished + "ms"); } @Override protected void onPause(long millisUntilFinished) { // 倒计时暂停 Log.d("CountDown", "Paused with " + millisUntilFinished + "ms remaining"); } @Override protected void onResume(long millisUntilFinished) { // 倒计时恢复 Log.d("CountDown", "Resumed with " + millisUntilFinished + "ms remaining"); } @Override protected void onCancel(long millisUntilFinished) { // 倒计时取消 Log.d("CountDown", "Cancelled with " + millisUntilFinished + "ms remaining"); } @Override protected void onFinish(long millisDuration) { // 倒计时完成 Log.d("CountDown", "Finished!"); } }; // 启动 countDownTimer.start(); // 暂停 countDownTimer.pause(); // 恢复 countDownTimer.resume(); // 取消 countDownTimer.cancel();
一个轻量级的单次延迟工具。tick() 触发一次延迟任务,回调触发后自动结束。
// 创建:500ms 后触发一次回调 AndroidTick tick = new AndroidTick(500L) { @Override protected void onTick(AndroidTick thisInstance, int count) { // count 为累计触发次数 Log.d("Tick", "Triggered " + count + " times"); } }; // 触发延迟任务(若已有待执行任务,此次调用被静默忽略) tick.tick(); // 取消上一次任务并重新计时 tick.tick(true); // 取消待执行的回调 tick.cancel();
构造参数
millisDelay必须大于 0,否则抛出IllegalArgumentException。可通过isRunning()查询是否有待执行任务。
典型场景:搜索框防抖(用户停止输入后 500ms 才发起请求)、延迟隐藏 Toast。
AndroidTimer(long interval) AndroidTimer(long interval, AndroidTimer.Option option)
interval必须大于 0,否则抛出IllegalArgumentException。省略option时使用默认配置(tickWhenStart = false)。
start(): 启动计时器pause(): 暂停计时器resume(): 恢复计时器cancel(): 取消计时器getState(): 获取当前状态getInterval(): 获取当前 tick 间隔setInterval(long newInterval, int mode): 修改 tick 间隔,任意状态下均可调用;返回boolean表示是否生效——newInterval为非正数时不生效并返回false,与当前间隔相同时为无操作并返回true;mode取值见 2.5 IntervalChangeMode
millisFly:计时器运行的已飞逝毫秒数(不含暂停时间)
onStart(long millisFly): 计时器启动时调用onTick(long millisFly): 每个间隔触发onPause(long millisFly): 计时器暂停时调用onResume(long millisFly): 计时器恢复时调用onCancel(long millisFly): 计时器取消时调用
CountDownTimer(long millisDuration, long interval) CountDownTimer(long millisDuration, long interval, CountDownTimer.Option option)
millisDuration与interval均必须大于 0,否则抛出IllegalArgumentException。省略option时使用默认配置(tickWhenStart = false、tickWhenFinish = false)。
start(): 启动倒计时pause(): 暂停倒计时resume(): 恢复倒计时cancel(): 取消倒计时getState(): 获取当前状态getFutureDuration(): 获取倒计时总时长getInterval(): 获取当前 tick 间隔setInterval(long newInterval, int mode): 修改 tick 间隔,任意状态下均可调用;返回boolean表示是否生效——newInterval为非正数时不生效并返回false,与当前间隔相同时为无操作并返回true;若在IMMEDIATE模式下新的下一次 tick 时机已越过结束时间,将立即走结束流程(先按tickWhenFinish决定是否触发一次onTick(0),再触发onFinish(0))。mode取值见 2.5 IntervalChangeMode
onStart(long millisUntilFinished): 倒计时开始时调用onTick(long millisUntilFinished): 每个间隔触发onPause(long millisUntilFinished): 倒计时暂停时调用onResume(long millisUntilFinished): 倒计时恢复时调用onCancel(long millisUntilFinished): 倒计时取消时调用onFinish(long millisDuration): 倒计时自然结束时调用,参数恒为0;通过cancel()中止的倒计时不会触发此回调
单次延迟回调工具,无状态机、无暂停/恢复。
AndroidTick(long millisDelay)
millisDelay必须大于 0,否则抛出IllegalArgumentException。
tick(): 触发一次延迟任务;若已有待执行任务,本次调用被静默忽略tick(boolean forceCancel):forceCancel = true时先取消待执行任务再重新计时;false时等价于tick()cancel(): 取消待执行的回调isRunning(): 是否有待执行任务
onTick(AndroidTick thisInstance, int count): 延迟到达时调用。thisInstance为当前实例;count为累计触发次数,从 1 开始,cancel()不会将其重置
AndroidTimer.Option option = new AndroidTimer.Option( boolean tickWhenStart // 启动时是否立即触发一次onTick );
另有无参构造
new AndroidTimer.Option(),等价于new AndroidTimer.Option(false)。
CountDownTimer.Option option = new CountDownTimer.Option( boolean tickWhenStart, // 启动时是否立即触发一次onTick boolean tickWhenFinish // 完成时是否触发一次onTick );
另有无参构造
new CountDownTimer.Option(),等价于new CountDownTimer.Option(false, false)。Kotlin DSL 的option参数默认值即为该无参构造。
setInterval(long newInterval, int mode) 的 mode 参数控制新间隔何时生效,两个计时器共用同一枚举:
IntervalChangeMode.IMMEDIATE:以上次 tick 为基准按新间隔重算下一次 tick(即上次 tick + newInterval),再钳到当前时刻(RUNNING为调用时刻,PAUSED为暂停时刻)之后;也就是说,调小间隔若新的 tick 时机尚未到达则对齐上次 tick、若已越过当前时刻则立即触发并从触发点重锚,调大间隔同样对齐上次 tick。PAUSED状态无法触发,因而钳到暂停时刻的逾期 tick 会在resume()的瞬间触发(两者在 fly-time 上等价)。IntervalChangeMode.NEXT_TICK:不影响已排定的下一次 tick,新间隔从该 tick 之后开始生效
⚠️ 0.4.3 起IMMEDIATE语义变更(破坏性):0.4.2 及更早版本是「以当前时刻为基准」(now + newInterval)重新计算;自 0.4.3 改为上述「以上次 tick 为基准」。若原先依赖旧行为,请留意升级后下一次 tick 时机的差异。
timer.setInterval(500L, IntervalChangeMode.IMMEDIATE); timer.setInterval(500L, IntervalChangeMode.NEXT_TICK);
AndroidTimer 与 CountDownTimer 共用以下状态(AndroidTick 无状态机):
- IDLE: 初始状态,计时器未启动
- RUNNING: 运行中状态
- PAUSED: 暂停状态
状态转换图:
stateDiagram-v2
[*] --> IDLE
IDLE --> RUNNING: start()
RUNNING --> PAUSED: pause()
PAUSED --> RUNNING: resume()
PAUSED --> RUNNING: start() 重启
RUNNING --> IDLE: cancel()
PAUSED --> IDLE: cancel()
RUNNING --> IDLE: 自然结束(仅 CountDownTimer)
start():仅在RUNNING状态下被静默忽略。IDLE下正常启动;PAUSED下会完整重启——计时基准归零,已累计的millisFly(倒计时则为剩余时长)被丢弃,等同于一次全新的start()。- 若只想从
PAUSED继续,请用resume(),不要用start()。 - 从
RUNNING重置计时器,需先cancel()归位至IDLE,再start()。 pause()仅在RUNNING下生效,resume()仅在PAUSED下生效,cancel()在IDLE下无效——三者在错误状态下均静默忽略。CountDownTimer倒计时自然结束时状态自动回到IDLE并触发onFinish,此路径不触发onCancel。
- minSdk 21 / compileSdk 36
- 构建需 JDK 17+(字节码目标 Java 8)
- Gradle 8.0+(当前 8.13)
- Kotlin 2.0.21、coroutines 1.9.0(仅
android-timer-kt)
./gradlew build
./gradlew test提供 DSL 构建函数和 Flow 接口,适用于 Kotlin 项目。Kotlin 类位于 com.github.xesam.android.timer.kt 包,使用时按需导入:
import com.github.xesam.android.timer.kt.androidTimer import com.github.xesam.android.timer.kt.countDownTimer import com.github.xesam.android.timer.kt.androidTick import com.github.xesam.android.timer.kt.TimerState
// 纯 DSL 用法 val timer = androidTimer(1000L, onTick = { ms -> updateUI(ms) }) timer.start() timer.pause() timer.resume() timer.cancel() // Flow 用法(观测 tick 和状态) val timer = androidTimer(1000L) lifecycleScope.launch { timer.tickFlow.collect { ms -> updateProgress(ms) } } lifecycleScope.launch { timer.stateFlow.collect { state -> when (state) { TimerState.RUNNING -> showRunning() TimerState.PAUSED -> showPaused() TimerState.IDLE -> showIdle() } } } timer.start()
也可直接读取
timer.state: TimerState获取当前状态(等价于stateFlow.value),无需订阅 Flow。
onFinish回调与finishFlow均可单独使用,任选其一即可;此示例仅展示两者可共存。
// DSL + Flow 混用 val countdown = countDownTimer( duration = 10_000L, interval = 100L, onFinish = { showResult() } ) lifecycleScope.launch { countdown.tickFlow.collect { ms -> updateCountdown(ms) } } lifecycleScope.launch { countdown.finishFlow.collect { navigateToResult() } } countdown.start()
val tick = androidTick(500L, onTick = { count -> Log.d("Tick", "Triggered $count times") }) tick.tick() // 触发延迟任务 tick.tick(true) // 取消上次,重新计时 tick.cancel() // 取消待执行回调
Kt*类均继承了对应 Java 类的全部方法。例如KtAndroidTimer/KtCountDownTimer同样可调用setInterval(newInterval, mode)(见 2.5 IntervalChangeMode)、getInterval()、getState()等。
setInterval(long newInterval, int mode)签名由void改为返回boolean:非正数间隔不再抛异常,改为不生效并返回false;与当前间隔相同时为无操作并返回true(构造函数仍对非正数间隔抛IllegalArgumentException)- 破坏性变更:
IntervalChangeMode.IMMEDIATE语义由「以当前时刻为基准」改为「以上次 tick 为基准」重算下一次 tick,详见 2.5 IntervalChangeMode
- 文档修正:新增 §2.3 AndroidTick 正式 API 章节;补全两个计时器的构造参数校验、
Option无参构造说明 - 状态图与 §3.1 补充
PAUSED下start()会完整重启、CountDownTimer自然结束回到IDLE两条真实行为;澄清onFinish语义(参数恒为 0、cancel()不触发) android-timer-kt将kotlinx-coroutines依赖由implementation改为api,修复消费者使用tickFlow/stateFlow/finishFlow时缺失 Flow 类型的问题
AndroidTimer/CountDownTimer新增setInterval(long newInterval, int mode),支持运行时动态修改 tick 间隔,mode取值IntervalChangeMode.IMMEDIATE/NEXT_TICK,详见 2.5 IntervalChangeMode
- Kotlin 扩展独立为
android-timer-kt模块,类迁移至com.github.xesam.android.timer.kt包 - Kotlin 侧新增
state: TimerState只读属性与TimerState枚举,替代 Java 继承的getState() - 修复
CountDownTimer结束路径在tickWhenFinish=true时重复触发onTick(0)的问题 AndroidTick构造校验延迟 > 0,新增isRunning()公开状态查询- 回调(
onStart/onTick/onPause/onResume/onCancel/onFinish)改为在同步锁外派发,避免用户回调重入引发状态惊奇 - 接入 detekt/checkstyle 静态检查
- 依赖升级至 Kotlin 2.0.21、coroutines 1.9.0、Gradle 8.13
Copyright 2025-2026 xesam
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.
如有问题或建议,请通过以下方式联系:
- GitHub Issues: 创建Issue
