zoukankan      html  css  js  c++  java
  • 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(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);
        }
    }
  • 相关阅读:
    [二叉树算法]关于层次遍历二叉树的一些算法总结
    数据库事务并发访问产生的问题及四种事务隔离级别
    当relative遇上z-index,阻断事件捕获
    关于CAS操作
    LRU算法与LRUCache
    Hadoop2.x 关于日志文件位置
    推荐系统架构图——我的软件工程概论课设
    文件上传+解析漏洞
    命令执行漏洞
    SSRF漏洞
  • 原文地址:https://www.cnblogs.com/ixenos/p/10552961.html
Copyright © 2011-2022 走看看