@author ixenos 2019-03-18 16:26:38
一、原理
1.将所有Handler注册到池中
1.普通Handler在handlers数组中
2.callLatter的Handler在laters数组中
2.然后按定义的执行时刻(或执行帧)进行循环判断执行
3.通过映射浏览器的requestAnimationFrame进行全局帧循环
4.Timer中再自行根据执行时刻(或执行帧)实现Laya框架的时间循环(或帧循环),即Laya引擎的时钟。
二、源码解析
(注意,本文对应Laya 2.0版本,Laya 2.0以上对Timer中的callLater的逻辑进行了解耦合,而在Laya 1.7中,是将callater的时钟处理放在Timer中的)
(而解耦合后callLater本身已不依赖Timer,此时留接口在Timer中是为了兼容2.0以前的代码而已)
1 package laya.utils { 2 3 /** 4 * <code>Timer</code> 是时钟管理类。它是一个单例,不要手动实例化此类,应该通过 Laya.timer 访问。 5 */ 6 public class Timer { 7 8 /**@private */ 9 private static var _pool:Array = []; 10 /**@private */ 11 public static var _mid:int = 1; 12 13 /*[DISABLE-ADD-VARIABLE-DEFAULT-VALUE]*/ 14 /** 时针缩放。*/ 15 public var scale:Number = 1; 16 /** 当前帧开始的时间。*/ 17 public var currTimer:Number = Browser.now(); 18 /** 当前的帧数。*/ 19 public var currFrame:int = 0; 20 /**@private 两帧之间的时间间隔,单位毫秒。*/ 21 public var _delta:int = 0; 22 /**@private */ 23 public var _lastTimer:Number = Browser.now(); 24 /**@private */ 25 private var _map:Array = []; 26 /**@private */ 27 private var _handlers:Array = []; 28 /**@private */ 29 private var _temp:Array = []; 30 /**@private */ 31 private var _count:int = 0; 32 33 /** 34 * 创建 <code>Timer</code> 类的一个实例。 35 */ 36 public function Timer(autoActive:Boolean = true) { 37 autoActive && Laya.systemTimer && Laya.systemTimer.frameLoop(1, this, _update); 38 } 39 40 /**两帧之间的时间间隔,单位毫秒。*/ 41 public function get delta():int { 42 return _delta; 43 } 44 45 /** 46 * @private 47 * 帧循环处理函数。 48 */ 49 public function _update():void { 50 if (scale <= 0) { 51 _lastTimer = Browser.now(); 52 return; 53 } 54 var frame:int = this.currFrame = this.currFrame + scale; 55 var now:Number = Browser.now(); 56 _delta = (now - _lastTimer) * scale; 57 var timer:Number = this.currTimer = this.currTimer + _delta; 58 _lastTimer = now; 59 60 //处理handler 61 var handlers:Array = this._handlers; 62 _count = 0; 63 for (var i:int = 0, n:int = handlers.length; i < n; i++) { 64 var handler:TimerHandler = handlers[i]; 65 if (handler.method !== null) { 66 var t:int = handler.userFrame ? frame : timer; 67 if (t >= handler.exeTime) { 68 if (handler.repeat) { 69 if (!handler.jumpFrame) { 70 handler.exeTime += handler.delay; 71 handler.run(false); 72 if (t > handler.exeTime) { 73 //如果执行一次后还能再执行,做跳出处理,如果想用多次执行,需要设置jumpFrame=true 74 handler.exeTime += Math.ceil((t - handler.exeTime) / handler.delay) * handler.delay; 75 } 76 } else { 77 while (t >= handler.exeTime) { 78 handler.exeTime += handler.delay; 79 handler.run(false); 80 } 81 } 82 } else { 83 handler.run(true); 84 } 85 } 86 } else { 87 _count++; 88 } 89 } 90 91 if (_count > 30 || frame % 200 === 0) _clearHandlers(); 92 } 93 94 /** @private */ 95 private function _clearHandlers():void { 96 var handlers:Array = this._handlers; 97 for (var i:int = 0, n:int = handlers.length; i < n; i++) { 98 var handler:TimerHandler = handlers[i]; 99 if (handler.method !== null) _temp.push(handler); 100 else _recoverHandler(handler); 101 } 102 this._handlers = _temp; 103 handlers.length = 0; 104 _temp = handlers; 105 } 106 107 /** @private */ 108 private function _recoverHandler(handler:TimerHandler):void { 109 if (_map[handler.key] == handler) _map[handler.key] = null; 110 handler.clear(); 111 _pool.push(handler); 112 } 113 114 /** @private */ 115 public function _create(useFrame:Boolean, repeat:Boolean, delay:int, caller:*, method:Function, args:Array, coverBefore:Boolean):TimerHandler { 116 //如果延迟为0,则立即执行 117 if (!delay) { 118 method.apply(caller, args); 119 return null; 120 } 121 122 //先覆盖相同函数的计时 123 if (coverBefore) { 124 var handler:TimerHandler = _getHandler(caller, method); 125 if (handler) { 126 handler.repeat = repeat; 127 handler.userFrame = useFrame; 128 handler.delay = delay; 129 handler.caller = caller; 130 handler.method = method; 131 handler.args = args; 132 handler.exeTime = delay + (useFrame ? this.currFrame : this.currTimer + Browser.now() - _lastTimer); 133 return handler; 134 } 135 } 136 137 //找到一个空闲的timerHandler 138 handler = _pool.length > 0 ? _pool.pop() : new TimerHandler(); 139 handler.repeat = repeat; 140 handler.userFrame = useFrame; 141 handler.delay = delay; 142 handler.caller = caller; 143 handler.method = method; 144 handler.args = args; 145 handler.exeTime = delay + (useFrame ? this.currFrame : this.currTimer + Browser.now() - _lastTimer); 146 147 //索引handler 148 _indexHandler(handler); 149 150 //插入数组 151 _handlers.push(handler); 152 153 return handler; 154 } 155 156 /** @private */ 157 private function _indexHandler(handler:TimerHandler):void { 158 var caller:* = handler.caller; 159 var method:* = handler.method; 160 var cid:int = caller ? caller.$_GID || (caller.$_GID = Utils.getGID()) : 0; 161 var mid:int = method.$_TID || (method.$_TID = (_mid++) * 100000); 162 handler.key = cid + mid; 163 _map[handler.key] = handler; 164 } 165 166 /** 167 * 定时执行一次。 168 * @param delay 延迟时间(单位为毫秒)。 169 * @param caller 执行域(this)。 170 * @param method 定时器回调函数。 171 * @param args 回调参数。 172 * @param coverBefore 是否覆盖之前的延迟执行,默认为 true 。 173 */ 174 public function once(delay:int, caller:*, method:Function, args:Array = null, coverBefore:Boolean = true):void { 175 _create(false, false, delay, caller, method, args, coverBefore); 176 } 177 178 /** 179 * 定时重复执行。 180 * @param delay 间隔时间(单位毫秒)。 181 * @param caller 执行域(this)。 182 * @param method 定时器回调函数。 183 * @param args 回调参数。 184 * @param coverBefore 是否覆盖之前的延迟执行,默认为 true 。 185 * @param jumpFrame 时钟是否跳帧。基于时间的循环回调,单位时间间隔内,如能执行多次回调,出于性能考虑,引擎默认只执行一次,设置jumpFrame=true后,则回调会连续执行多次 186 */ 187 public function loop(delay:int, caller:*, method:Function, args:Array = null, coverBefore:Boolean = true, jumpFrame:Boolean = false):void { 188 var handler:TimerHandler = _create(false, true, delay, caller, method, args, coverBefore); 189 if (handler) handler.jumpFrame = jumpFrame; 190 } 191 192 /** 193 * 定时执行一次(基于帧率)。 194 * @param delay 延迟几帧(单位为帧)。 195 * @param caller 执行域(this)。 196 * @param method 定时器回调函数。 197 * @param args 回调参数。 198 * @param coverBefore 是否覆盖之前的延迟执行,默认为 true 。 199 */ 200 public function frameOnce(delay:int, caller:*, method:Function, args:Array = null, coverBefore:Boolean = true):void { 201 _create(true, false, delay, caller, method, args, coverBefore); 202 } 203 204 /** 205 * 定时重复执行(基于帧率)。 206 * @param delay 间隔几帧(单位为帧)。 207 * @param caller 执行域(this)。 208 * @param method 定时器回调函数。 209 * @param args 回调参数。 210 * @param coverBefore 是否覆盖之前的延迟执行,默认为 true 。 211 */ 212 public function frameLoop(delay:int, caller:*, method:Function, args:Array = null, coverBefore:Boolean = true):void { 213 _create(true, true, delay, caller, method, args, coverBefore); 214 } 215 216 /** 返回统计信息。*/ 217 public function toString():String { 218 return " handlers:" + _handlers.length + " pool:" + _pool.length; 219 } 220 221 /** 222 * 清理定时器。 223 * @param caller 执行域(this)。 224 * @param method 定时器回调函数。 225 */ 226 public function clear(caller:*, method:Function):void { 227 var handler:TimerHandler = _getHandler(caller, method); 228 if (handler) { 229 _map[handler.key] = null; 230 handler.key = 0; 231 handler.clear(); 232 } 233 } 234 235 /** 236 * 清理对象身上的所有定时器。 237 * @param caller 执行域(this)。 238 */ 239 public function clearAll(caller:*):void { 240 if (!caller) return; 241 for (var i:int = 0, n:int = _handlers.length; i < n; i++) { 242 var handler:TimerHandler = _handlers[i]; 243 if (handler.caller === caller) { 244 _map[handler.key] = null; 245 handler.key = 0; 246 handler.clear(); 247 } 248 } 249 } 250 251 /** @private */ 252 private function _getHandler(caller:*, method:*):TimerHandler { 253 var cid:int = caller ? caller.$_GID || (caller.$_GID = Utils.getGID()) : 0; 254 var mid:int = method.$_TID || (method.$_TID = (_mid++) * 100000); 255 return _map[cid + mid]; 256 } 257 258 /** 259 * 延迟执行。 260 * @param caller 执行域(this)。 261 * @param method 定时器回调函数。 262 * @param args 回调参数。 263 */ 264 public function callLater(caller:*, method:Function, args:Array = null):void { 265 CallLater.I.callLater(caller, method, args); 266 } 267 268 /** 269 * 立即执行 callLater 。 270 * @param caller 执行域(this)。 271 * @param method 定时器回调函数。 272 */ 273 public function runCallLater(caller:*, method:Function):void { 274 CallLater.I.runCallLater(caller, method); 275 } 276 277 /** 278 * 立即提前执行定时器,执行之后从队列中删除 279 * @param caller 执行域(this)。 280 * @param method 定时器回调函数。 281 */ 282 public function runTimer(caller:*, method:Function):void { 283 var handler:TimerHandler = _getHandler(caller, method); 284 if (handler && handler.method != null) { 285 _map[handler.key] = null; 286 handler.run(true); 287 } 288 } 289 290 /** 291 * 暂停时钟 292 */ 293 public function pause():void { 294 this.scale = 0; 295 } 296 297 /** 298 * 恢复时钟 299 */ 300 public function resume():void { 301 this.scale = 1; 302 } 303 } 304 } 305 306 /** @private */ 307 class TimerHandler { 308 public var key:int; 309 public var repeat:Boolean; 310 public var delay:int; 311 public var userFrame:Boolean; 312 public var exeTime:int; 313 public var caller:* 314 public var method:Function; 315 public var args:Array; 316 public var jumpFrame:Boolean; 317 318 public function clear():void { 319 caller = null; 320 method = null; 321 args = null; 322 } 323 324 public function run(withClear:Boolean):void { 325 var caller:* = this.caller; 326 if (caller && caller.destroyed) return clear(); 327 var method:Function = this.method; 328 var args:Array = this.args; 329 withClear && clear(); 330 if (method == null) return; 331 args ? method.apply(caller, args) : method.call(caller); 332 } 333 }