Laya Timer原理 & 源码解析

Laya Timer原理 & 源码解析

@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 }
View Code

 

1.创建实例的时候:

        /**
         * 创建 <code>Timer</code> 类的一个实例。
         */
        public function Timer(autoActive:Boolean = true) {
            autoActive && Laya.systemTimer && Laya.systemTimer.frameLoop(1, this, _update);
        }

将_update注册为帧循环函数,在frameLoop中会将其包裹为一个TimerHandler

 

2.而_update本身是处理TimerHandler的函数:

        /**
         * @private
         * 帧循环处理函数。
         */
        public function _update():void {
            if (scale <= 0) {
                _lastTimer = Browser.now();
                return;
            }
            var frame:int = this.currFrame = this.currFrame + scale;
            var now:Number = Browser.now();
            _delta = (now - _lastTimer) * scale;
            var timer:Number = this.currTimer = this.currTimer + _delta;
            _lastTimer = now;
            
            //处理handler
            var handlers:Array = this._handlers;
            _count = 0;
            for (var i:int = 0, n:int = handlers.length; i < n; i++) {
                var handler:TimerHandler = handlers[i];
                if (handler.method !== null) {
                    var t:int = handler.userFrame ? frame : timer;
                    if (t >= handler.exeTime) {
                        if (handler.repeat) {
                            if (!handler.jumpFrame) {
                                handler.exeTime += handler.delay;
                                handler.run(false);
                                if (t > handler.exeTime) {
                                    //如果执行一次后还能再执行,做跳出处理,如果想用多次执行,需要设置jumpFrame=true
                                    handler.exeTime += Math.ceil((t - handler.exeTime) / handler.delay) * handler.delay;
                                }
                            } else {
                                while (t >= handler.exeTime) {
                                    handler.exeTime += handler.delay;
                                    handler.run(false);
                                }
                            }
                        } else {
                            handler.run(true);
                        }
                    }
                } else {
                    _count++;
                }
            }
            
            if (_count > 30 || frame % 200 === 0) _clearHandlers();
        }

在一次执行中,会遍历所有handlers,判断执行条件(时刻、帧等)进行执行

 

3._update由Stage.render调用,Stage.render通过Stage._loop调用,Stage._loop由是Render中的enter_frame处理器调用,

.........................................................................................

Laya.Stage
.........................................................................................
          /**@private */
          public function _loop():Boolean {
            render(Render._context, 0, 0);
            return true;
          }

......
/**@inheritDoc */ override public function render(context:Context, x:Number, y:Number):void { if (_frameRate === FRAME_SLEEP) { var now:Number = Browser.now(); if (now - _frameStartTime >= 1000) _frameStartTime = now; else return; } _renderCount++; if (!this._visible) { if (_renderCount % 5 === 0) { CallLater.I._update(); Stat.loopCount++; Laya.systemTimer._update(); Laya.startTimer._update(); Laya.physicsTimer._update(); Laya.updateTimer._update(); Laya.lateTimer._update(); Laya.timer._update(); } return; } ....... ......................................................................................... Laya.Stage .........................................................................................

 

 

.........................................................................................

Laya.Render
.........................................................................................


     public function Render(width:Number, height:Number) { //创建主画布。改到Browser中了,因为为了runtime,主画布必须是第一个 _mainCanvas.source.id = "layaCanvas"; _mainCanvas.source.width = width; _mainCanvas.source.height = height; Browser.container.appendChild(_mainCanvas.source); RunDriver.initRender(_mainCanvas, width, height); Browser.window.requestAnimationFrame(loop); function loop(stamp:Number):void { Laya.stage._loop(); Browser.window.requestAnimationFrame(loop); } Laya.stage.on("visibilitychange", this, _onVisibilitychange); } /**@private */ private var _timeId:int = 0; /**@private */ private function _onVisibilitychange():void { if (!Laya.stage.isVisibility) { _timeId = Browser.window.setInterval(this._enterFrame, 1000); } else if (_timeId != 0) { Browser.window.clearInterval(_timeId); } }

.........................................................................................

Laya.Render
.........................................................................................
 

 

 

4.顺便看看callLater呗:

package laya.utils {
    
    /**
     * @private
     */
    public class CallLater {
        public static var I:CallLater =/*[STATIC SAFE]*/ new CallLater();
        /**@private */
        private var _pool:Array = [];
        /**@private */
        private var _map:Array = [];
        /**@private */
        private var _laters:Array = [];
        
        /**
         * @private
         * 帧循环处理函数。
         */
        public function _update():void {
            var laters:Array = this._laters;
            var len:int = laters.length;
            if (len > 0) {
                for (var i:int = 0, n:int = len - 1; i <= n; i++) {
                    var handler:LaterHandler = laters[i];
                    _map[handler.key] = null;
                    if (handler.method !== null) {
                        handler.run();
                        handler.clear();
                    }
                    _pool.push(handler);
                    i === n && (n = laters.length - 1);
                }
                laters.length = 0;
            }
        }
        
        /** @private */
        private function _getHandler(caller:*, method:*):LaterHandler {
            var cid:int = caller ? caller.$_GID || (caller.$_GID = Utils.getGID()) : 0;
            var mid:int = method.$_TID || (method.$_TID = (Timer._mid++) * 100000);
            return _map[cid + mid];
        }
        
        /**
         * 延迟执行。
         * @param    caller 执行域(this)。
         * @param    method 定时器回调函数。
         * @param    args 回调参数。
         */
        public function callLater(caller:*, method:Function, args:Array = null):void {
            if (_getHandler(caller, method) == null) {
                if (_pool.length)
                    var handler:LaterHandler = _pool.pop();
                else handler = new LaterHandler();
                //设置属性
                handler.caller = caller;
                handler.method = method;
                handler.args = args;
                //索引handler
                var cid:int = caller ? caller.$_GID : 0;
                var mid:int = method["$_TID"];
                handler.key = cid + mid;
                _map[handler.key] = handler
                //插入队列
                _laters.push(handler);
            }
        }
        
        /**
         * 立即执行 callLater 。
         * @param    caller 执行域(this)。
         * @param    method 定时器回调函数。
         */
        public function runCallLater(caller:*, method:Function):void {
            var handler:LaterHandler = _getHandler(caller, method);
            if (handler && handler.method != null) {
                _map[handler.key] = null;
                handler.run();
                handler.clear();
            }
        }
    }
}

/** @private */
class LaterHandler {
    public var key:int;
    public var caller:*
    public var method:Function;
    public var args:Array;
    
    public function clear():void {
        caller = null;
        method = null;
        args = null;
    }
    
    public function run():void {
        var caller:* = this.caller;
        if (caller && caller.destroyed) return clear();
        var method:Function = this.method;
        var args:Array = this.args;
        if (method == null) return;
        args ? method.apply(caller, args) : method.call(caller);
    }
}

 

posted @ 2019-03-18 17:14  ixenos  阅读(1735)  评论(0编辑  收藏  举报