Android实现倒计时的方案梳理
作者:newki 发布时间:2022-04-29 00:48:31
前言
关于倒计时可以说我们App开发中常见的一种场景了,比如Splash倒计时跳转首页,比如发送短信之后倒计时60秒显示等等。
关于倒计时的实现方式,大家可能有不同的做法,这里做一下总结看看你使用的是哪一种呢?
一、CountDownTimer的实现
直接上代码:
//倒计时的方式一
fun countDownTimer() {
var num = 60
timer = object : CountDownTimer((num + 1) * 1000L, 1000L) {
override fun onTick(millisUntilFinished: Long) {
YYLogUtils.w("当时计数:" + num)
if (num == 0) {
YYLogUtils.w("重新开始")
num = 60
} else {
num--
}
}
override fun onFinish() {
YYLogUtils.w("倒计时结束了..." + num)
}
}
timer?.start()
}
private var timer: CountDownTimer? = null
override fun onDestroy() {
super.onDestroy()
timer?.cancel()
}
没什么花活,就是android.os包下面的 CountDownTimer 类的使用。内部实现使用了 Handler 进行封装。
二、直接用Handler的实现
private var handlerNum = 60
private val mHandler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
1 -> {
if (handlerNum > 0) {
handlerNum--
YYLogUtils.w("当时计数:" + handlerNum)
countDownHander()
} else {
stopCountDownHander()
}
}
}
}
}
override fun onDestroy() {
super.onDestroy()
stopCountDownHander()
}
fun countDownHander() {
mHandler.sendEmptyMessageDelayed(1, 1000)
}
fun stopCountDownHander() {
mHandler.removeCallbacksAndMessages(null)
}
我们可以直接使用Handler的延时发送消息实现倒计时。
当然另一种做法是使用 Runnable 来实现:
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
recLen++;
txtView.setText("" + recLen);
handler.postDelayed(this, 1000);
}
public void test(){
handler.postDelayed(runnable, 1000);
}
三、直接用Time、TimeTask的实现
以上是Android的倒计时方案,其实Java的Api也是支持倒计时实现的,比如 Timer 配合 TimerTask 就可以实现简单的倒计时。
fun countDownTimer2() {
var num = 60
val timer = Timer()
val timeTask = object : TimerTask() {
override fun run() {
num--
YYLogUtils.w("当时计数:" + num)
if (num < 0) {
timer.cancel()
}
}
}
timer.schedule(timeTask, 1000, 1000)
}
四、使用Theard倒计时
我们可以通过Thread的sleep方法来实现倒计时,不过由于是子线程我们不能更新UI,所以还是需要配合Handler实现。
private var mThread: Thread = Thread(this)
private var mflag = false
private var mThreadNum = 60
override fun run() {
while (mflag && mThreadNum >= 0) {
try {
Thread.sleep(1000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
val message = Message.obtain()
message.what = 1
message.arg1 = mThreadNum
handler.sendMessage(message)
mThreadNum--
}
}
private val handler = Handler(Looper.getMainLooper()) { msg ->
if (msg.what == 1) {
val num = msg.arg1
//由于需要主线程显示UI,这里使用Handler通信
YYLogUtils.w("当时计数:" + num)
}
true
}
//开启倒计时
fun countDownThread() {
if (!mThread.isAlive) {
mflag = true
if (mThread.state == Thread.State.TERMINATED) {
mThread = Thread(this@DemoCountDwonActivity)
if (mThreadNum == -1) mThreadNum = 60
mThread.start()
} else {
mThread.start()
}
} else {
mflag = false
}
}
override fun onDestroy() {
super.onDestroy()
mflag = false
}
这里的销毁线程我没有使用stop方法,已经不推荐我们使用,我们使用flag来判断即可。
五、使用框架RxJava
这样的线程并不是我们想要的,我们通常并不会直接new Thread 来进行一些逻辑操作,比如我们可能使用RxJava框架,通过操作符的方式来进行倒计时。
比我们倒计时4秒之后跳转页面的实现:
val SHOTDOWN_TIME = 4
val mDisposables : Disposable? = null
Observable.interval(0, 1, TimeUnit.SECONDS)
.take(SHOTDOWN_TIME.toLong())
.map {
return@map SHOTDOWN_TIME - it
}
.observeOn(AndroidSchedulers.mainThread())
.subscribe({
LogUtil.e(it.toString())
}, {
it.printStackTrace()
}, {
checkJump()
}, {
mDisposable = it
})
override fun onDestroy() {
super.onDestroy()
mDisposable?.dispose()
}
注意:我们还是需要通过mDisposable对象在页面销毁的时候释放,以免内存泄露,有没有简单一点方式?
六、Kotlin Flow 的实现
上面的方法都需要销毁资源,好麻烦,能不能自动取消?协程不就行了。
是的 lifecycleScope 根据生命周期自动取消的协程作用域,配合Flow的操作符完成倒计时岂不是完美。
好吧,你是自动倒计时了。结束之后取消协程,销毁也能取消协程,那如果我想手动的取消倒计时怎么办?比如倒计时60秒我就要在第50秒的时候强制取消协程怎么办?
launch方法返回的不就是Job 对象吗?根据此上下文对象不就可以取消协程了吗?
看看灵活的Flow倒计时如何实现。
定义一个扩展方法:
/**
* 倒计时的实现
*/
@ExperimentalCoroutinesApi
fun FragmentActivity.countDown(
time: Int = 5,
start: (scop: CoroutineScope) -> Unit,
end: () -> Unit,
next: (time: Int) -> Unit
) {
lifecycleScope.launch {
// 在这个范围内启动的协程会在Lifecycle被销毁的时候自动取消
flow {
(time downTo 0).forEach {
delay(1000)
emit(it)
}
}.onStart {
// 倒计时开始 ,在这里可以让Button 禁止点击状态
start(this@launch)
}.onCompletion {
// 倒计时结束 ,在这里可以让Button 恢复点击状态
end()
}.catch {
//错误
YYLogUtils.e(it.message ?: "Unkown Error")
}.collect {
// 在这里 更新值来显示到UI
next(it)
}
}
}
使用:
fun startCountDown() {
var timeDownScope: CoroutineScope? = null
countDown(
time = 60,
start = {
timeDownScope = it
YYLogUtils.e("开始")
},
end = {
YYLogUtils.e("结速倒计时")
toast("结速倒计时")
},
next = {
YYLogUtils.w("当时计数:" + it)
if (it == 50) {
timeDownScope?.cancel()
}
})
}
无需onDestory中销毁资源,如果想自由手动的控制倒计时,我们再start的高阶函数中接收父协程的上下文对象即可自动控制。使用起来也是超级简单。
来源:https://juejin.cn/post/7128947531471388709


猜你喜欢
- 相信大家都遇到过,自己的Java应用运行一段时间就宕机了或者响应请求特别慢。这时候就需要我们了来找出问题所在了。绝大部分都是代码问题导致的。
- 1、包装类型是什么?Java 为每一个基本数据类型都引入了对应的包装类型,int 的包装类就是 Integer,从 Java 5 开始引入了
- 今天实现了一个模拟碟片加载过程的小demo,在此展示一下。由于在公司,不好截取动态图片,因此就在这截取两张静态图片看看效果先。下面简单的将代
- Java 常量池的实例详解Java的常量池中包含了类、接口、方法、字符串等一系列常量值。常量池在编译期间就已经确定,并保存在*.class文
- final可以修饰类 ,成员变量,局部变量和方法。1.final修饰成员变量1.final成员变量的初始化对于final修饰的变量,系统不会
- mybatis-plus-generator + clickhouse 自动生成代码依赖<!--> mybatis-plus &
- 原理解析:利用RandomAccessFile在本地创建一个随机访问文件,文件大小和服务器要下载的文件大小相同。 根据线程的数量(假设有三个
- import java.util.LinkedList;public class OJ { public OJ() {
- 这篇文章主要介绍了springboot日期转换器实现实例解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需
- 问题背景昨晚同事找我帮他看一个问题,他使用mybatis-plus中提供的updateById方法,想将查询结果中某个字段原本不为null的
- 1.非静态成员变量当成员变量为非静态成员变量且对当前类进行实例化时,将会产生死循环例子:public class ConstructorCl
- 很多导航菜单是树形的,即一级一级往下分,这样的结构固然需要用递归来处理。 对于Freemarker 来说,宏就相当于函数,其定义了签名及参数
- 本文实例为大家分享了Android Studio实现带边框的圆形头像的具体代码,供大家参考,具体内容如下效果显示:(没有边框的)(有边框的)
- 使用maven的profile功能,我们可以实现多环境配置文件的动态切换,可参考我的上一篇博客。但随着SpringBoot项目越来越火,越来
- 一、概述项目中经常用到倒计时的功能,比如说限时抢购,手机获取验证码等等。而google官方也帮我们封装好了一个类:CountDownTime
- 加载图片openCv有一个名imread的简单函数,用于从文件中读取图像imread 函数位于Imgcodecs类的同名包中。加载图片代码i
- 前言之前写的progress其实根本没有起到进度条的作用,太显眼,而且并不好看,所以有了新的想法,我们将ProgressBar控件换成See
- 一、什么是代理?指为一个目标对象提供一个代理对象, 并由代理对象控制对目标对象的引用. 使用代理对象, 是为了在不修改目标对象的基础上,增强
- 一、使用Pull解析器读取XML文件除了可以使用SAX或DOM解析XML文件之外,大家也可以使用Android内置的Pull解析器解析XML
- 发送虚拟请求访问controller我们在test类中虚拟访问controller,就得发送虚拟请求。先创建一个controllerpack