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);
    }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM