Minimização do uso da CPU

Outra importante área de foco para otimização é o uso da CPU. A otimização do processamento de CPU aumenta o desempenho, e como resultado, o ciclo de vida da bateria em dispositivos remotos.

Aprimoramentos do Flash Player 10.1 para uso da CPU

O Flash Player 10.1 introduz dois novos recursos que ajudam a economizar processamento da CPU. Os recursos envolvem pausar e retomar conteúdo SWF quando este estiver fora da tela e limitar o número de instâncias do Flash Player em uma página.

Pausa, limitação e retomada

Nota: O recurso de pausa, limitação e retomada não se aplica aos aplicativos do Adobe® AIR®.

Para otimizar o uso da CPU e da bateria, o Flash Player 10.1 introduz um novo recurso relacionado a instâncias inativas. Este recurso permite que você limite o uso da CPU pausando e reiniciando o arquivo SWF quando o conteúdo chegar ao fim na tela. Com este recurso, o Flash Player libera tanta memória quanto possível removendo quaisquer objetos que possam ser recriados quando a reprodução do conteúdo é retomada. O conteúdo é considerado fora da tela quando todo o conteúdo estiver fora da tela.

Dois cenários fazem com que o conteúdo SWF fique fora da tela:

  • O usuário rola a página e faz com que o conteúdo SWF se mova para fora da tela.

    Neste caso, se houver alguma reprodução de áudio ou vídeo, o conteúdo continuará sendo reproduzido, mas a renderização irá parar. Se não houver nenhuma reprodução de áudio ou vídeo, para assegurar que a reprodução ou a execução do ActionScript não sejam pausada, defina o parâmetro de HTML hasPriority como verdadeiro. Entretanto, lembre-se de que a renderização do conteúdo SWF será pausada quando o conteúdo ficar fora da tela ou oculto, independentemente do valor do parâmetro de HTML hasPriority .

  • Uma guia é aberta no navegador, o que faz com que o conteúdo SWF se mova para o segundo plano.

    Neste caso, independentemente do valor da marca de HTML hasPriority , o conteúdo de SWF terá sua velocidade diminuída, ou limitada , para 2 a 8 fps. A reprodução de áudio e vídeo é interrompida e nenhum conteúdo que está sendo renderizado é processado, a menos que o conteúdo de SWF se torne visível novamente.

Para o Flash Player 11.2 e posterior executado em navegadores de desktops Windows e Mac, você pode usar o ThrottleEvent no seu aplicativo. O Flash Player despacha um ThrottleEvent quando é pausado, limitado ou retoma a reprodução.

O ThrottleEvent é um evento de transmissão, ou seja, ele é despachado por todos os objetos EventDispatcher com um ouvinte registrado para esse evento. Para obter mais informações sobre eventos de transmissão, consulte a classe DisplayObject .

Gerenciamento de ocorrência

Nota: O recurso de gerenciamento de instâncias não se aplica aos aplicativos Adobe® AIR®.
Use o parâmetro HTML hasPriority para atrasar o carregamento de arquivos SWF que estejam fora da tela.

O Flash Player 10.1 introduz um novo parâmetro HTML chamado hasPriority :

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

Este recurso limita o número de ocorrências do Flash Player iniciadas em uma página. Limitar o número de ocorrências ajuda a conservar recursos de CPU e bateria. A ideia é atribuir uma prioridade específica ao conteúdo SWF, dando prioridade a algum conteúdo com relação a outros conteúdos na página. Considere um simples exemplo: um usuário está navegando por um site e a página de índice hospeda três arquivos SWF diferentes. Um deles é visível, outro é parcialmente visível na tela e o último fica fora da tela, sendo necessária rolagem. As duas primeiras animações têm início normalmente, mas a última é atrasada até que se torne visível. Este cenário é o comportamento padrão quando o parâmetro hasPriority não está presente ou está definida como false . Para garantir que um arquivo SWF seja iniciado, mesmo que esteja fora da tela, defina o parâmetro hasPriority como true . No entanto, independentemente do valor do parâmetro hasPriority , um arquivo SWF que não é visível ao usuário terá sua renderização sempre pausada.

Nota: Se os recursos de CPU disponíveis ficarem baixos, as instâncias do Flash Player não são mais iniciadas automaticamente, mesmo que o parâmetro hasPriority esteja definido como true . Se novas instâncias forem criadas por meio do JavaScript depois que a página for carregada, essas instâncias ignorarão o sinalizador hasPriority . Qualquer conteúdo de 1x1 pixel ou 0x0 pixel é iniciado, o que impede arquivos SWF de ajuda de serem atrasados caso o webmaster deixe de incluir o sinalizador hasPriority . No entanto, arquivos SWF podem ainda ser iniciados quando clicados. Esse comportamento é chamado de “clique para reproduzir”.

Os seguintes diagramas mostram os efeitos de se configurar o parâmetro hasPriority para valores diferentes:

Efeitos de valores diferentes do parâmetro hasPriority

Efeitos de valores diferentes do parâmetro hasPriority

Modo de suspensão

O Flash Player 10.1 e o AIR 2.5 introduzem um novo recurso em dispositivos móveis que ajuda a economizar processamento da CPU e, como resultado, a vida útil da bateria. Esse recurso envolve a luz de fundo encontrada em muitos dispositivos móveis. Por exemplo, se um usuário que executa um aplicativo móvel for interrompido e parar de usar o dispositivo, o tempo de execução detecta quando a luz e fundo entra em modo de suspensão. Em seguida, ele reduz a taxa de quadros para 4 quadros por segundo (fps) e pausa a renderização. Para aplicativos AIR, o modo de suspensão também começa quando o aplicativo passa para o segundo plano.

O código ActionScript continua a ser executado no modo de suspensão, de modo semelhante a definir a propriedade Stage.frameRate como 4 fps. Mas a etapa de renderização é ignorada, de forma que o usuário não possa ver que o player está sendo executado a 4 fps. Uma taxa de quadros de 4 fps foi escolhida em vez de zero, por permitir que todas as conexões permaneçam abertas (NetStream, Socket e NetConnection). Alternar para zero quebraria as conexões abertas. Uma taxa de atualização de 250 ms foi escolhida (4 fps) porque muitos fabricantes de dispositivos usam essa taxa de quadros como taxa de atualização. Usar esse valor mantém a taxa de quadros do tempo de execução com o mesmo valor aproximado do próprio dispositivo.

Nota: Quando o tempo de execução estiver no modo de suspensão, a propriedade Stage.frameRate retornará a taxa de quadros do arquivo SWF original, em vez de 4 fps.

Quando a luz de fundo volta para o modo ligado, a renderização é retomada. A taxa de quadros retorna para seu valor original. Considere um aplicativo de media player em que um usuário está reproduzindo uma música. Se a tela passar para o modo de suspensão, o tempo de execução responderá com base no tipo de conteúdo que está sendo reproduzido. Eis uma lista de situações com o comportamento correspondente do tempo de execução:

  • A luz de fundo entra no modo de suspensão e conteúdo que não é de áudio nem vídeo está sendo reproduzido: a renderização é pausada e a taxa de quadros é definida como 4 fps.

  • A luz de fundo entra no modo de suspensão e conteúdo de áudio/vídeo está sendo reproduzido: o tempo de execução força a luz de fundo a ficar sempre acesa, dando continuidade à experiência do usuário.

  • A luz de fundo passa do modo de suspensão para o modo ligado: o tempo de execução define a taxa de quadros com a configuração de taxa de quadros original do arquivo SWF e retoma a renderização.

  • O Flash Player é pausado enquanto conteúdo de áudio/vídeo é reproduzido: o Flash Player redefine o estado da luz de fundo para o comportamento padrão do sistema, já que o áudio/vídeo não está mais em reprodução.

  • O dispositivo móvel recebe uma chamada telefônica enquanto conteúdo de áudio/vídeo é reproduzido: a renderização é pausada e a taxa de quadros é definido como 4 fps.

  • O modo de suspensão da luz de fundo é desativado em um dispositivo móvel: o tempo de execução se comporta normalmente.

Quando a luz de fundo entra no modo de suspensão, a renderização é pausada e a taxa de quadros é desacelerado. Este recurso economiza o processamento da CPU, mas não se pode depender dele para criar uma pausa real, como em um aplicativo de jogo.

Nota: Nenhum evento ActionScript é enviado quando o tempo de execução entra ou sai do modo de suspensão.

Congelando e descongelando objetos

Congelar e descongelar objetos da maneira correta utilizando os eventos REMOVED_FROM_STAGE e ADDED_TO_STAGE .

Para otimizar seu código, sempre congele e descongele seus objetos. Congelar e descongelar são importantes para todos os objetos, mas são especialmente importantes para objetos de exibição. Mesmo que os objetos de exibição não estejam mais na lista de exibição e estiverem aguardando serem coletados como lixo, ainda podem estar usando código que consome muita CPU. Por exemplo, ainda podem estar usando Event.ENTER_FRAME. Como resultado, é importante congelar e descongelar os objetos utilizando os eventos Event.REMOVED_FROM_STAGE e Event.ADDED_TO_STAGE . O exemplo a seguir mostra um clipe de filme reproduzindo em um palco que interage como o teclado:

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

Exibir gráfico inteiro
Clipe que interaje com o teclado

Quando o botão Remover é clicado, o clipe de filme é removido da lista de exibição:

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

Mesmo quando removido da lista de exibição, o clipe de filme ainda aciona o evento Event.ENTER_FRAME . O clipe de filme ainda é executado, mas não renderizado. Para lidar corretamente com esta situação, ouça os eventos apropriados e remova os ouvintes de eventos para evitar que códigos intensos de CPU sejam executados.

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

Quando o botão Exibir é pressionado, o clipe de filme é reiniciado ele volta a ouvir eventos Event.ENTER_FRAME novamente e o teclado controla o corretamente o clipe de filme.

Nota: Se um objeto de exibição for removido da lista de exibição, definir sua referência como null após a remoção não garante que o objeto seja congelado. Se o coletor de lixo não for executado, o objeto continua a consumir memória e processamento da CPU, mesmo que o objeto não seja mais exibido. Para garantir que o objeto consuma o mínimo de processamento de CPU possível, certifique-se de congelá-lo completamente ao removê-lo da lista de exibição.

A partir do Flash Player 10 e do AIR 1.5, o comportamento a seguir também ocorre. Se a cabeça de reprodução encontrar um quadro vazio, o objeto de exibição é congelado automaticamente, mesmo que você não tenha implementado nenhum comportamento de congelamento.

O conceito de congelamento também é importante ao carregar conteúdo remoto com a classe Loader. Ao usar a classe Loader com o Flash Player 9 e o AIR 1.0, era necessário congelar manualmente o conteúdo ouvindo o evento Event.UNLOAD emitido pelo objeto LoaderInfo LoaderInfo. Cada objeto precisava ser congelado manualmente, o que não era uma tarefa trivial. O Flash Player 10 e o AIR 1.5 introduzem um importante novo método na classe Loader chamado unloadAndStop() . Esse método permite descarregar um arquivo SWF, congelar automaticamente cada objeto no arquivo SWF carregado e forçar a execução do coletor de lixo.

No código a seguir, o arquivo SWF é carregado e então descarregado pelo método unload() , que requer mais processamento e congelamento manual:

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

Uma melhor prática é usar o método unloadAndStop() , que lida com o congelamento nativamente e força a execução do processo de coleta de lixo:

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

As ações a seguir ocorrem quando o método unloadAndStop() é chamado:

  • Os sons são parados.

  • Os ouvintes registrados na linha de tempo principal do arquivo SWF são removidos.

  • Objetos Timer são parados.

  • Dispositivos periféricos de hardware (como câmera e microfone) são liberados.

  • Cada clipe de filme é parado.

  • O acionamento de Event.ENTER_FRAME , Event.FRAME_CONSTRUCTED , Event.EXIT_FRAME , Event.ACTIVATE e Event.DEACTIVATE é parado.

Ativar e desativar eventos

Use os eventos Event.ACTIVATE e Event.DEACTIVATE para detectar inatividade em segundo plano e otimizar seu aplicativo devidamente.

Dois eventos ( Event.ACTIVATE e Event.DEACTIVATE ) podem ajudar você a fazer ajustes finos em seu aplicativo, de modo a usar o menor número de ciclos da CPU possível. Esses eventos permitem detectar quando o tempo de execução ganha ou perde o foco. Como resultado, o código pode ser otimizado de modo a reagir a alterações de contexto. O código a seguir ouve ambos os eventos e altera dinamicamente a taxa de quadros para zero quando o aplicativo perde o foco. Por exemplo, a animação pode perder o foco quando o usuário alternar para outra guia ou colocar o aplicativo em segundo plano:

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

Quando o aplicativo volta a receber o foco, a taxa de quadros é redefinida para seu valor original. Em vez de mudar a taxa de quadros dinamicamente, você também pode considerar fazer outras otimizações, como o congelamento e o descongelamento de objetos.

Os eventos activate e deactivate permitem implementar um mecanismo semelhante no recurso "Pausar e retomar" algumas vezes encontrado em dispositivos móveis e netbooks.

Interação com mouse

Considere desabilitar interações como mouse, quando possível.

Quando estiver usando um objeto interativo, como um objeto MovieClip ou Sprite, o tempo de execução executa código nativo para detectar e lidar com interações de mouse. A detecção de interações de mouse pode ser intensivo para a CPU quando muitos objetos interativos são exibidos na tela, especialmente se eles se sobrepõe. Uma maneira fácil de evitar este processamento é desabilitar interações de mouse em objetos que não precisam de interações do mouse. O código a seguir ilustra o uso das propriedades mouseEnabled e 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;

Quando possível, considere desabilitar as interações de mouse, o que ajuda seu aplicativo a usar menos processamento de CPU e, como resultado, reduz o uso da bateria.

Timers X eventos ENTER_FRAME

Escolha entre temporizadores ou eventos ENTER_FRAME , dependendo de qual conteúdo é animado.

Temporizadores tem preferência sobre eventos Event.ENTER_FRAME para conteúdo não animado executados por longo tempo.

No ActionScript 3.0, existem duas maneiras de chamar uma função em intervalos específicos. A primeira abordagem é utilizar o evento Event.ENTER_FRAME despachado por objetos de exibição (DisplayObject). A segunda aproximação é usar um temporizador. Desenvolvedores de ActionScript usam com frequência a aproximação de evento ENTER_FRAME . O evento ENTER_FRAME é enviado em cada quadro. Como resultado, o intervalo no qual a função é chamada esta relacionada com o quadro a taxa de quadros atual. A taxa de quadros pode ser acessada através da propriedade Stage.frameRate . No entanto, em alguns casos, a utilização de um temporizador pode ser uma escolha melhor do sobre o evento ENTER_FRAME . Por exemplo, se você não utilizar animação, mas quiser que seu código seja chamado em intervalos específicos, a utilização de um timer pode ser uma escolha melhor.

Um temporizador pode se comportar de uma maneira similar a um evento ENTER_FRAME , mas um evento pode ser enviado sem estar atrelado a taxa de quadros. Este comportamento pode oferecer alguma otimização significativa. Considere uma aplicação de reprodutor de vídeo como um exemplo. Neste caso, você não precisa utilizar uma taxa de quadros alta, porque apenas os controles do aplicativo estão se movendo.

Nota: A taxa de quadros não afeta o vídeo, porque o vídeo não está incorporado à linha do tempo. Em vez disso, o vídeo é carregado dinamicamente através de download progressivo ou streaming.

Neste exemplo, a taxa de quadros é definido para um valor baixo de 10 fps. O temporizador atualiza os controles a uma taxa de uma atualização por segundo. A taxa de atualização mais alta é possível por conta do método updateAfterEvent() , que está disponível no objeto TimerEvent. Este método força a tela a ser atualizada a cada vez que o temporizador envia um evento, caso seja necessário. O código a seguir ilustra a ideia:

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

Chamar o método updateAfterEvent() não modifica a taxa de quadros. Isto apenas força o tempo de execução a atualizar o conteúdo que mudou na tela. A linha do tempo continua em 10 fps. Lembre-se que temporizadores e eventos ENTER_FRAME não são muito precisos em dispositivos de baixo desempenho, ou se as funções do manipulador de eventos contiverem código que necessita processamento pesado. Assim como a taxa de quadros do arquivo SWF, a taxa de atualização de quadros do temporizador pode variar em algumas situações.

Minimize o número de objetos do cronômetro e operadores enterFrame registrados no aplicativo.

Em cada quadro, o tempo de execução despacha o evento enterframe para cada objeto de exibição na lista de exibição. Embora você possa registrar os ouvintes para o evento enterFrame com múltiplos objetos de exibição, isso indica que mais códigos são executados em cada quadro. Em vez disso, considere a utilização de um operador simples centralizado enterframe que executa todos os códigos para executar cada quadro. Centralizando esse código, é mais fácil gerenciar todos os códigos que executam com frequência.

De forma semelhante, se estiver utilizando objetos do cronômetro, há sobrecarga associada à criação e ao despacho de eventos de múltiplos objetos do cronômetro. Se você ativar diferentes operações em intervalos diferentes, aqui estão algumas sugestões de alternativas:

  • Utilize o número mínimo de objetos do cronômetro e operações de grupo de acordo com a frequência em que ocorrem .

    Por exemplo, utilize um cronômetro em operações frequentes; defina para acionar a cada 100 milissegundos. Utilize outro cronômetro para operações menos frequentes ou em segundo plano, defina para acionar a cada 2.000 milissegundos.

  • Utilize um objeto cronômetro simples e faça as operações acionarem em múltiplos do intervalo da propriedade delay do objeto cronômetro.

    Por exemplo, suponha que você tem algumas operações que espera que ocorram a cada 100 milissegundos e outras que deseja que ocorram a cada 200 milissegundos. Nesse caso, utilize um objeto cronômetro simples com valor delay de 100 milissegundos. No operador de eventos timer , adicione a instrução condicional que somente executa operações de 200 milissegundos a cada hora. O exemplo abaixo demonstra esta técnica:

    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; 
    }
Pare os objetos do cronômetro quando não estão em uso.

Se um operador de eventos timer do objeto Timer desempenhar somente operações em certas condições, invoque o método stop() do objeto Timer se nenhuma condição for verdadeira.

No evento enterFrame ou nos operadores do cronômetro, diminua o número de alterações na aparência dos objetos de exibição que fazem com que a tela seja redesenhada.

Em cada quadro, a fase de renderização redesenha a parte da fase que foi alterada durante esse quadro. Se a área redesenhada for grande, ou se for pequena mas tiver uma grande quantidade de objetos ou objetos de exibição complexos, o tempo de execução precisa de mais tempo para a renderização. Para testar a quantidade de redesenho necessário, utilize o recurso "mostrar regiões de redesenho" na depuração no Flash Player ou AIR.

Para obter mais informações sobre como melhorar o desempenho para ações repetidas, consulte o seguinte artigo:

Síndrome de interpolação

Para poupar potência de CPU, limite o uso de interpolação, o que poupa processamento de CPU, memória e duração de bateria.

Designers e desenvolvedores produzindo conteúdo para Flash no desktop tendem a utilizar muita interpolação de movimento em seus aplicativos. Quando você estiver produzindo conteúdo para dispositivos móveis de baixo desempenho, tente minimizar o uso de interpolação de movimentos. Limitar o seu uso ajuda o conteúdo a ser executado mais rápido em dispositivos menos sofisticados.