最大程度减小 CPU 使用量

优化的另一个重要部分是 CPU 使用量。优化 CPU 处理可提高性能,从而延长移动设备上的电池寿命。

针对 CPU 使用量的 Flash Player 10.1 增强功能

Flash Player 10.1 引入了两个有助于减少 CPU 处理的新功能。这些功能包括:SWF 内容在屏幕外时暂停和恢复播放,限制页面上的 Flash Player 实例数。

暂停、节流和恢复

注: 暂停、节流和恢复功能不适用于 Adobe® AIR® 应用程序。

为了优化 CPU 和电池使用量,Flash Player 10.1 引入了一个与非活动实例相关的新功能。 此功能是当内容位于屏幕之外和在屏幕上时暂停和恢复 SWF 文件,从而允许您限制 CPU 使用量。借助此功能,Flash Player 通过删除在恢复播放内容时可能重新创建的任何对象,来释放尽可能多的内存。当全部内容都位于屏幕之外时,才将该内容视为位于屏幕之外。

以下两种情况会导致 SWF 内容位于屏幕之外:

  • 用户滚动页面,导致 SWF 内容移动到屏幕之外。

    在这种情况下,如果播放任何音频或视频,内容将继续播放,但呈现会停止。如果没有播放任何音频或视频,要确保播放或 ActionScript 执行不会暂停,请将 hasPriority HTML 参数设置为 true。但是,请记住,当内容在屏幕以外或隐藏时,无论 hasPriority HTML 参数为何值,SWF 内容呈现均将暂停。

  • 在浏览器中打开了一个选项卡,导致 SWF 内容移动到背景中。

    在这种情况下,无论 hasPriority HTML 标签为何值,SWF 内容将降速或 节流 到 2 和 8 fps 之间。除非 SWF 内容再次可见,否则音频和视频播放将停止并且不处理任何内容呈现。

对于运行在 Windows 和 Mac 台式机浏览器上的 Flash Player 11.2 和更高版本,可以在应用程序中使用 ThrottleEvent。Flash Player 暂停、节流或恢复播放时调度 ThrottleEvent。

此 ThrottleEvent 事件为广播事件,这意味着将由所有具有注册了此事件的侦听器的 EventDispatcher 对象调度此事件。有关广播事件的详细信息,请参阅 DisplayObject 类。

实例管理

注: 实例管理功能不适用于 Adobe® AIR® 应用程序。
使用 hasPriority HTML 参数可延迟加载位于屏幕之外的 SWF 文件。

Flash Player 10.1 引入了一个名为 hasPriority 的新 HTML 参数:

<param name="hasPriority" value="true" />

此功能限制在页面上启动的 Flash Player 实例的数量。限制实例的数量有助于节省 CPU 和电池资源。其目的是将特定优先级分配给 SWF 内容,使某些内容的优先级高于页面上的其他内容。请看一个简单的示例:一个用户正在浏览网站,而索引页面承载着三个不同的 SWF 文件。其中一个文件可见,一个在屏幕上部分可见,最后一个位于屏幕之外,需要滚动页面才能看到。前两个动画可以正常启动,但最后一个动画被延迟到其变为可见时启动。当 hasPriority 参数不存在或被设置为 false 时,这种情况是默认行为。为了确保能够启动 SWF 文件(甚至该文件位于屏幕之外),请将 hasPriority 参数设置为 true 。然而,不管 hasPriority 参数的值是多少,始终暂停呈现用户看不到的 SWF 文件。

注: 如果可用的 CPU 资源量降低,将不再自动启动 Flash Player 实例,即使将 hasPriority 参数设置为 true 也是如此。如果新实例是在加载此页后通过 JavaScript 创建的,这些实例将忽略 hasPriority 标志。如果网站管理员无法添加 hasPriority 标志,则会启动任何 1x1 像素或 0x0 像素内容,阻止辅助 SWF 文件延迟。然而,仍可通过单击启动 SWF 文件。此行为称为“单击播放”。

下图显示将 hasPriority 参数设置为不同值的效果:

不同 hasPriority 参数值的效果

不同 hasPriority 参数值的效果

睡眠模式

Flash Player 10.1 和 AIR 2.5 对移动设备引入了一个新功能,有助于节省 CPU 处理,从而提高电池寿命。此功能涉及到许多移动设备上存在的背景光。例如,如果运行移动应用程序的用户被中断并停止使用设备,运行时可检测背景光进入睡眠模式的时间。然后将帧速率降低到 4 帧/秒 (fps) 并暂停呈现。对于 AIR 应用程序,还会在应用程序进入背景状态时开始睡眠模式。

ActionScript 代码在睡眠模式下继续执行,这与将 Stage.frameRate 属性设置为 4 fps 类似。但是跳过呈现步骤,因此用户看不到该播放器正在以 4 fps 的速率运行。之所以将帧速率选择为 4 fps (而不是 0),是因为该速率允许所有连接保持打开状态(NetStream、Socket 和 NetConnection)。将帧速率切换到 0 fps 会断开打开的连接。之所以将刷新频率选择为 250 毫秒 (4 fps),是因为许多设备制造商使用此帧频率作为其刷新频率。使用此值可以使运行时的帧频率与设备本身保持在同一范围。

注: 当运行时处于睡眠模式时, Stage.frameRate 属性将返回原始 SWF 文件的帧速率,而不是 4 fps。

当背景光重新进入打开模式时,呈现将恢复。帧速率将恢复其原始值。以用户正在播放音乐的媒体播放器应用程序为例。如果屏幕进入睡眠模式,运送时将根据正在播放的内容类型做出响应。以下是对应的运行时行为的情况列表:

  • 背景光进入睡眠模式并且正在播放非 A/V 内容:暂停呈现并将帧速率设置为 4 fps。

  • 背景光进入睡眠模式并且正在播放 A/V 内容:运行时强制始终打开背景光,继续供用户使用。

  • 背景光从睡眠模式进入打开模式:运行时将帧速率恢复为原始 SWF 文件帧速率设置并恢复呈现。

  • Flash Player 在播放 A/V 内容时暂停:Flash Player 将背景光状态重置为默认的系统行为,因为不再播放 A/V。

  • 移动设备在播放 A/V 内容时接收到电话呼叫:暂停呈现并将帧速率设置为 4 fps。

  • 对移动设备禁用背景光睡眠模式:运行时正常运行。

当背景光进入睡眠模式时,呈现暂停且帧速率减慢。此功能可节省 CPU 处理,但就像在游戏应用程序中一样,不能依赖该功能进行实际暂停。

注: 运行时进入或退出睡眠模式时,不会调度 ActionScript 事件。

冻结和解冻对象

使用 REMOVED_FROM_STAGE ADDED_TO_STAGE 事件正确冻结和解冻对象。

要优化您的代码,请始终冻结和解冻您的对象。冻结和解冻对所有对象都很重要,但对显示对象尤其重要。即使显示对象不再位于显示列表中并正在等待作为垃圾回收,其代码仍然占用大量 CPU。例如,它们仍然在使用 Event.ENTER_FRAME。因此,使用 Event.REMOVED_FROM_STAGE Event.ADDED_TO_STAGE 事件正确冻结和解冻对象非常关键。以下示例显示一个在舞台上播放的、与键盘交互的影片剪辑:

// Listen to keyboard events 
stage.addEventListener(KeyboardEvent.KEY_DOWN, keyIsDown); 
stage.addEventListener(KeyboardEvent.KEY_UP, keyIsUp); 
  
// Create object to store key states 
var keys:Dictionary = new Dictionary(true); 
  
function keyIsDown(e:KeyboardEvent):void 
{ 
    // Remember that the key was pressed 
    keys[e.keyCode] = true;     
  
    if (e.keyCode==Keyboard.LEFT || e.keyCode==Keyboard.RIGHT) 
    { 
        runningBoy.play(); 
    } 
} 
  
function keyIsUp(e:KeyboardEvent):void 
{ 
    // Remember that the key was released 
    keys[e.keyCode] = false; 
  
    for each (var value:Boolean in keys) 
          if ( value ) return; 
    runningBoy.stop(); 
} 
  
runningBoy.addEventListener(Event.ENTER_FRAME, handleMovement); 
runningBoy.stop(); 
  
var currentState:Number = runningBoy.scaleX; 
var speed:Number = 15; 
  
function handleMovement(e:Event):void 
{ 
    if (keys[Keyboard.RIGHT]) 
    { 
        e.currentTarget.x += speed; 
        e.currentTarget.scaleX = currentState;     
    } else if (keys[Keyboard.LEFT]) 
    { 
        e.currentTarget.x -= speed; 
        e.currentTarget.scaleX = -currentState; 
    } 
}

查看完全大小图形
与键盘交互的影片剪辑

单击“Remove”按钮后,将从显示列表中删除该影片剪辑:

// Show or remove running boy 
showBtn.addEventListener (MouseEvent.CLICK,showIt); 
removeBtn.addEventListener (MouseEvent.CLICK,removeIt); 
 
function showIt (e:MouseEvent):void 
{ 
    addChild (runningBoy); 
} 
 
function removeIt(e:MouseEvent):void 
{ 
    if (contains(runningBoy)) removeChild(runningBoy); 
}

即使将该影片剪辑从显示列表中删除,它仍会调度 Event.ENTER_FRAME 事件。影片剪辑仍在运行,但不会呈现。要正确处理这种情况,请侦听正确的事件并删除事件侦听器,以阻止执行占用大量 CPU 资源的代码:

// Listen to Event.ADDED_TO_STAGE and Event.REMOVED_FROM_STAGE 
runningBoy.addEventListener(Event.ADDED_TO_STAGE,activate); 
runningBoy.addEventListener(Event.REMOVED_FROM_STAGE,deactivate); 
 
function activate(e:Event):void 
{ 
    // Restart everything 
    e.currentTarget.addEventListener(Event.ENTER_FRAME,handleMovement); 
} 
 
function deactivate(e:Event):void 
{ 
    // Freeze the running boy - consumes fewer CPU resources when not shown 
    e.currentTarget.removeEventListener(Event.ENTER_FRAME,handleMovement); 
    e.currentTarget.stop(); 
}

按下“Show”按钮后,将重新启动影片剪辑,它将再次侦听 Event.ENTER_FRAME 事件且键盘将正确控制该影片剪辑。

注: 如果将显示对象从显示列表中删除,则完成删除后将其引用设置为 null 不能确保该对象是冻结的。如果不运行垃圾回收器,则对象将继续占用内存和 CPU 处理,即使此对象不再显示。要确保对象占用的 CPU 处理最少,请确保在将其从显示列表中删除后完全将其冻结。

使用 Flash Player 10 和 AIR 1.5 启动时,还会发生以下行为。如果播放头遇到一个空帧,将自动冻结显示对象,即使没有实施任何冻结行为也是如此。

冻结的概念在使用 Loader 类加载远程内容时也很重要。在 Flash Player 9 和 AIR 1.0 中使用 Loader 类时,必须通过侦听由 LoaderInfo 对象调度的 Event.UNLOAD 事件来手动冻结内容。必须手动冻结每个对象,这是一项至关重要的任务。Flash Player 10 和 AIR 1.5 对 Loader 类引入了一个重要的新方法,称为 unloadAndStop() 。通过此方法,可以卸载 SWF 文件、自动冻结加载的 SWF 文件中的每个对象并强制运行垃圾回收器。

以下代码将加载 SWF 文件,然后使用 unload() 方法将其卸载,这需要更多的处理操作和手动冻结:

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with no automatic object deactivation 
    // All deactivation must be processed manually 
    loader.unload(); 
}

最好使用 unloadAndStop() 方法,该方法以本机方式处理冻结并强制运行垃圾回收过程:

var loader:Loader = new Loader(); 
 
loader.load ( new URLRequest ( "content.swf" ) ); 
 
addChild ( loader ); 
 
stage.addEventListener ( MouseEvent.CLICK, unloadSWF ); 
 
function unloadSWF ( e:MouseEvent ):void 
{ 
    // Unload the SWF file with automatic object deactivation 
    // All deactivation is handled automatically 
    loader.unloadAndStop(); 
}

调用 unloadAndStop() 方法时将执行下列操作:

  • 停止声音。

  • 将删除注册到 SWF 文件的主时间轴中的侦听器。

  • 停止 Timer 对象。

  • 释放硬件外围设备(如摄像头和麦克风)。

  • 停止每个影片剪辑。

  • 停止调度 Event.ENTER_FRAME Event.FRAME_CONSTRUCTED Event.EXIT_FRAME Event.ACTIVATE Event.DEACTIVATE

激活和停用事件

使用 Event.ACTIVATE Event.DEACTIVATE 事件检测后台是否处于非活动状态,并相应地优化应用程序。

有两个事件( Event.ACTIVATE Event.DEACTIVATE )可以帮助您微调应用程序以使其尽量使用最少的 CPU 周期。这些事件允许您检测运行时获得或失去焦点的时间。因此可以对代码进行优化,以便对上下文更改做出反应。下列代码侦听这两种事件,并在应用程序失去焦点时动态地将帧频率更改为零。例如,动画可能在用户切换到另一个制表符或将应用程序放入后台时失去焦点:

var originalFrameRate:uint = stage.frameRate; 
var standbyFrameRate:uint = 0; 
  
stage.addEventListener ( Event.ACTIVATE, onActivate ); 
stage.addEventListener ( Event.DEACTIVATE, onDeactivate ); 
  
function onActivate ( e:Event ):void 
{ 
    // restore original frame rate 
    stage.frameRate = originalFrameRate; 
} 
  
function onDeactivate ( e:Event ):void 
{ 
    // set frame rate to 0 
    stage.frameRate = standbyFrameRate; 
}

当应用程序再次获得焦点时,帧频率会重置为原始值。除了动态更改帧速率,您还可以考虑进行其他优化,如冻结对象或解冻对象。

activate 和 deactivate 事件能让您实现与有时在移动设备或上网本上提供的“暂停和恢复”功能类似的机制。

鼠标交互

尽可能考虑禁用鼠标交互。

使用交互式对象(例如 MovieClip 或 Sprite 对象)时,运行时执行本机代码以检测和处理鼠标交互。当屏幕上显示许多交互式对象时,特别是当它们重叠时,检测鼠标交互可能会占用大量 CPU 资源。避免此处理的一种简便方法是对不需要任何鼠标交互的对象禁用鼠标交互。以下代码说明了 mouseEnabled mouseChildren 属性的用法:

// Disable any mouse interaction with this InteractiveObject 
myInteractiveObject.mouseEnabled = false; 
const MAX_NUM:int = 10; 
  
// Create a container for the InteractiveObjects 
var container:Sprite = new Sprite(); 
  
for ( var i:int = 0; i< MAX_NUM; i++ ) 
{ 
    // Add InteractiveObject to the container 
    container.addChild( new Sprite() ); 
} 
  
// Disable any mouse interaction on all the children 
container.mouseChildren = false;

尽可能考虑禁用鼠标交互,这有助于您的应用程序使用较少的 CPU 处理,从而在移动设备上减少电池使用量。

计时器与 ENTER_FRAME 事件

根据内容是否为动画,选择计时器或 ENTER_FRAME 事件。

对于执行时间太长的非动画内容,优先选择计时器,而不是 Event.ENTER_FRAME 事件。

在 ActionScript 3.0 中,有两种方法可以特定的间隔调用函数。第一种方法是使用由显示对象 (DisplayObject) 调度的 Event.ENTER_FRAME 事件。第二种方法是使用计时器。ActionScript 开发人员通常使用 ENTER_FRAME 事件方法。需要对每个帧调度 ENTER_FRAME 事件。因此,调用函数的间隔与当前帧速率有关。可通过 Stage.frameRate 属性来查看帧速率。然而,在某些情况下,使用计时器比使用 ENTER_FRAME 事件更合适。例如,如果您没有使用动画,但又想以特定的间隔调用代码,则使用计时器可能是更好的选择。

计时器的行为与 ENTER_FRAME 事件的行为类似,但是调度事件无需考虑帧速率。通过此行为,可实现一些重要优化。以视频播放器应用程序为例。在这种情况下,由于仅移动应用程序控件,不需要使用高帧速率。

注: 帧速率不影响视频,因为视频未嵌入时间轴中。视频是通过渐进式下载或流式下载动态加载的。

在此示例中,帧速率设置为一个较低的值 10 fps。计时器以每秒一次的速度更新控件。TimerEvent 对象中提供的 updateAfterEvent() 方法可以提供更高的更新速率。如果需要,此方法会在每次计时器调度事件时强制更新屏幕。以下代码演示了这一概念:

// Use a low frame rate for the application 
stage.frameRate = 10; 
  
// Choose one update per second 
var updateInterval:int = 1000; 
var myTimer:Timer = new Timer(updateInterval,0); 
  
myTimer.start(); 
myTimer.addEventListener( TimerEvent.TIMER, updateControls ); 
  
function updateControls( e:TimerEvent ):void 
{ 
    // Update controls here 
    // Force the controls to be updated on screen 
    e.updateAfterEvent(); 
}

调用 updateAfterEvent() 方法不会修改帧速率。它只强制运行时更新屏幕上已更改的内容。时间轴仍以 10 fps 的速度运行。请记住,在低性能设备上,或者事件处理函数包含要求进行大量处理的代码时,计时器和 ENTER_FRAME 事件并不完全精确。就像 SWF 文件帧速率一样,计时器的更新帧速率随情况的不同而不同。

将应用程序中 Timer 对象和注册的 enterFrame 处理函数的数量降至最少。

对于每个帧,运行时将为其显示列表中的每个显示对象调度一个 enterFrame 事件。尽管您可以使用多个显示对象为 enterFrame 事件注册侦听器,但这样做意味着将在每个帧上执行更多代码。或者,考虑使用一个集中的 enterFrame 处理函数,该函数执行要运行每个帧需要的所有代码。通过集中此类代码,更容易管理所有频繁运行的代码。

同样,如果使用的是 Timer 对象,将产生与从多个 Timer 对象创建和调度事件相关联的开销。如果您必须在不同的时间间隔触发不同的操作,以下提供了一些建议的替代方法:

  • 根据其发生的频率,使用最少数量的 Timer 对象和组操作。

    例如,将一个 Timer 对象用于频繁执行的操作,设置为每 100 毫秒触发一次。将另一个 Timer 对象用于频率较低的操作或后台操作,设置为每 2000 毫秒触发一次。

  • 使用一个 Timer 对象,并以 Timer 对象的 delay 属性时间间隔的倍数触发操作。

    例如,假设您希望某些操作每 100 毫秒发生一次,而其他操作每 200 毫秒发生一次。在这种情况下,请使用一个 delay 值为 100 毫秒的 Timer 对象。在 timer 事件处理函数中,添加一个条件语句,即仅每隔一次运行一次时间间隔为 200 毫秒的操作。以下示例对此技术进行了演示:

    var timer:Timer = new Timer(100); 
    timer.addEventListener(TimerEvent.Timer, timerHandler); 
    timer.start(); 
         
    var offCycle:Boolean = true; 
      
    function timerHandler(event:TimerEvent):void 
    { 
        // Do things that happen every 100 ms 
         
        if (!offCycle) 
        { 
            // Do things that happen every 200 ms 
        } 
         
        offCycle = !offCycle; 
    }
停止未使用的 Timer 对象。

如果 Timer 对象的 timer 事件处理函数仅在特定的条件下执行操作,则当不符合这些条件时调用 Timer 对象的 stop() 方法。

enterFrame 事件或 Timer 处理函数中,尽量减少对可导致重绘屏幕的显示对象外观的更改。

对于每个帧,呈现阶段都将重绘在该帧期间更改的舞台部分。如果重绘区域很大,或者很小但包含大量或复杂的显示对象,则运行时需要更多时间才能呈现。要测试需要重绘的量,请使用 Flash Player 调试版或 AIR 中的“显示重绘区域”功能。

有关提高重复操作的性能的详细信息,请参阅以下文章:

补间症状

要节省 CPU 电量,请限制补间的使用,这可以节省 CPU 处理、内存并延长电池寿命。

在台式机上生成 Flash 内容的设计人员和开发人员容易在其应用程序中使用许多补间动画。在性能较低的移动设备上生成内容时,尝试尽量减少使用补间动画。限制补间动画的使用有助于提高内容在低级设备上的运行速度。