吾日三省吾身见贤思齐焉 见不贤而内自省也。
年轻的时候做事没太多方法论,很多业务都是上来就构思+手写。少了借鉴这一步。最近越发觉得借鉴是个很关键的步骤。对自己的技术栈积累大有裨益。而且自己做的业务总是有切入点可以去模仿的。所以,以后还是多借鉴吧。顺便一说,Android 源码看似很多,实际也很多。不过看的越多也会越快。因为总是那么几个关键的“东西”不断的出现。随着时间推移,发现也就那么点“东西”。
Android 平台上 如何实现一个定时器
目前发现有两种流派:
- Thread Sleep派:我把他归类为直接让线程睡眠的方式。脑子里想想列出来的方式就是Thread,Timer,CountDownTimer,Rxjava.interval 等。这种方式是java原生类提供的方式。说白了就是需要额外的线程用于即使,通过睡一下完成各种业务。
- Handler派:也就是借助 android系统 handler + looper 那种方式。这种方式原理简述一下,android手机有一套用于屏幕绘制的一帧一帧的机制。 每一帧都有一个时间间隔。我自己不去创建任何额外的线程去计时。而是直接在每一帧绘制的时候都添加一个检查方法。从自己缓存的任务栈里面去比对当前时间和结束时间的大小关系。来判断是否计时完成。
太基础的Thread定时器很多了很容易搜到,我贴一个有一定业务逻辑的定时器吧
用线程实现一个输入过滤业务
所谓输入过滤:就是如果用连续输入,不触发任务,如果一但输入的停下的时间大于阈值,于是触发业务。
/**
* 持续输入过滤定时器
*/
class ContinuousTimerCounter(
var waitTime:Long ,
var sleepIntervalTime:Long ) {
var isStilling = false
private var endTime: Long = -1L;
private var obj:Any? = null
var callBack: ContinuousCallBack? = null
var isPaused = false
//暂停计时
fun pause(){
isPaused = true
}
//继续计时
fun resume(){
isPaused = false
newAction(this.obj)
}
//添加一个新值
fun newAction(obj: Any?){
this.obj = obj
if (isStilling) {
updateObj()
} else {
startTimeCount()
}
}
//开始计时
private fun startTimeCount() {
if (isStilling) return
updateObj()
isStilling = true
//开启Thread 然后 while 循环
Thread {
kotlin.run {
while (isStilling || isPaused) {
Thread.sleep(sleepIntervalTime)//睡眠的时间间隔任意可调,相当于时间的精度
if (endTime == -1L) {
continue
}
//判断时间差值来检查任务跳出
if (endTime < System.currentTimeMillis()) {
isStilling = false
}
}
callBack?.doAction(obj)
reset()
}
}.start()
}
private fun updateObj() {
endTime = System.currentTimeMillis() + waitTime
}
private fun reset() {//重置任务,也可用于退出
isStilling = false
endTime = -1L
}
fun isContinue(): Boolean {
return endTime > System.currentTimeMillis()
}
}
interface ContinuousCallBack {
fun doAction(obj:Any?)
}
属性动画,从setDuration开始发问,到通过帧率定时!
最新做业务用到了属性动画,属性动画有个api名为 setDuration(),这不就是一个定时器嘛。于是问题来了.他的定时器怎么实现的? 看源码之前先先做几个猜想
- 如果每个属性动画都搞一个定时器,岂不是线程分分钟挂掉,显然是不可能的!
- 如果那么维护一个Thread while循环sleep,不断检查任务入执行回调?可能是这样的
源码过程比较冗长,我就列几个关键字吧。感兴趣的顺着关键字看。
ValueAnimator.java
ValueAnimator.start()
ValueAnimator.setIntValues()
PropertyValuesHolder.java
ValueAnimator.setInterpolator()
ValueAnimator.setDuration()
ValueAnimator.doAnimationFrame()
ValueAnimator.animateBasedOnTime()
ValueAnimator.getAnimationHandler()
AnimationHandler.java
ThreadLocal<AnimationHandler> sAnimatorHandler
Choreographer.FrameCallback mFrameCallback
AnimationHandler#MyFrameCallbackProvider.class
mChoreographer.postCallback()
其实看到 Choreographer 我的问题就结束了。原来他也是我第二个猜想的模型, 不过他没有独立使用线程,而是使用的 android 司空见惯的 handler+loop 的机制。所谓 while 循环 sleep方案换成了,不断的帧率检查,检查添加到队列里的属性动画任务并去执行回调。没有使用sleep。 实际上这种方案对于百分之95的业务都是足够使用场景的。因为这个计时的精确度就是每一帧的间隔。足够了!
至此 源码阅读结束!如果下次业务有问题需要使用。我会考虑用第二种”帧检查“的方式试试。
哈哈,这个文章一切都从看到api setDuration() 开始的。毕竟提出一个好问题,问题就解决了一大半!据我和隔壁的ios同学讨论,他们的定时器主要都是用帧检查的方式实现!各个客户端可能都是同样的方案。如果读者有第三种方案,欢迎提出方向,共同切磋