Cierre e invocación de una aplicación de AIR

Adobe AIR 1.0 y posterior

En esta sección se analizan las formas en que se puede invocar una aplicación de Adobe® AIR® instalada, así­ como las opciones y consideraciones para cerrar una aplicación en ejecución.

Nota: los objetos NativeApplication, InvokeEvent y BrowserInvokeEvent solo están disponibles para el contenido SWF que se ejecuta en el entorno limitado de la aplicación de AIR. El contenido SWF que se ejecuta en el motor de ejecución de Flash Player, en el navegador o reproductor autónomo (proyector), o en una aplicación de AIR fuera del entorno limitado de la aplicación, no puede acceder a estas clases.

Para ver una explicación rápida y ejemplos de código de la invocación y finalización de aplicaciones de AIR, consulte los siguientes artículos de inicio rápido del Centro de desarrollo de Adobe:

Invocación de aplicaciones

Se invoca una aplicación se AIR cuando el usuario (o el sistema operativo):

  • inicia la aplicación desde el shell del escritorio;

  • utiliza la aplicación como comando en un shell de línea de comandos;

  • abre un tipo de archivo para el que la aplicación es la aplicación de apertura predeterminada;

  • (Mac OS X) hace clic en el icono de la aplicación en el Dock (esté ejecutándose en ese momento o no la aplicación);

  • elige iniciar la aplicación desde el programa de instalación (al finalizar un nuevo proceso de instalación o después de hacer doble clic en el archivo de AIR para una aplicación ya instalada);

  • inicia una actualización de una aplicación de AIR cuando la versión instalada ha señalado que está gestionando las actualizaciones por su cuenta (al incluir la declaración <customUpdateUI>true</customUpdateUI> en el archivo descriptor de la aplicación);

  • (iOS) Recibe una notificación del servicio de notificaciones push de Apple (APN).

  • Invoca la aplicación a través de una URL.

  • visita una página web que contiene un logotipo o una aplicación de Flash que llama al método com.adobe.air.AIR launchApplication() especificando la información de identificación para la aplicación de AIR. (Para que la invocación desde el navegador funcione, el descriptor de la aplicación debe incluir además una declaración <allowBrowserInvocation>true</allowBrowserInvocation>).

Siempre que se invoca una aplicación de AIR, AIR distribuye un objeto InvokeEvent del tipo invoke a través del objeto NativeApplication de instancia única. Para que la aplicación tenga tiempo de inicializarse y registrar un detector de eventos, los eventos invoke pasan a la cola en lugar de ser desechados. En cuanto se haya registrado el detector, se entregan todos los eventos que están en la cola.

Nota: cuando se invoca una aplicación con la función de invocación desde el navegador, el objeto NativeApplication solo distribuye un evento invoke si la aplicación no está ya ejecutándose.

Para recibir eventos invoke, llame al método addEventListener() del objeto NativeApplication (NativeApplication.nativeApplication). Cuando un detector de eventos se registra para un evento invoke, también recibe todos los eventos invoke que se produjeron antes de haberse registrado el detector. Los eventos invoke que están en la cola se distribuyen uno a la vez en un breve intervalo tras la devolución de la llamada a addEventListener(). Si se produce un nuevo evento invoke durante este proceso, puede que se distribuya antes de uno o varios de los eventos de la cola. Esta cola de eventos le permite controlar cualquier evento invoke que se haya producido antes de ejecutarse el código de inicialización. Tenga en cuenta que si se añade un detector de eventos más adelante en la ejecución (después de inicializada la aplicación), aún recibirá todos los eventos invoke que se hayan producido desde que se inició la aplicación.

Solo se inicia una instancia de una aplicación de AIR. Si se vuelve a invocar una aplicación que ya está en curso, AIR distribuye un nuevo evento invoke a la instancia que se está ejecutando. Es responsabilidad de una aplicación de AIR responder a un evento invoke y tomar las medidas correspondientes (por ejemplo, abrir una nueva ventana para documentos).

Un objeto InvokeEvent contiene los argumentos que se pasen a la aplicación, además del directorio desde el cual se invocó la aplicación. Si la aplicación se invocó debido a una asociación de tipo de archivo, los argumentos de la línea de comandos incluirán la ruta completa al archivo. Asimismo, si la aplicación se invocó a raíz de una actualización de la misma, se indica la ruta completa al archivo de actualización de AIR.

Cuando se abren varios archivos en una operación, se distribuye un solo objeto InvokeEvent en Mac OS X. Cada archivo se incluye en el conjunto arguments. En Windows y Linux, se distribuye un objeto InvokeEvent independiente para cada archivo.

La aplicación puede controlar eventos invoke registrando un detector con su objeto NativeApplication:

NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvokeEvent); 

Y mediante la definición de un detector de eventos:

var arguments:Array; 
var currentDir:File; 
public function onInvokeEvent(invocation:InvokeEvent):void { 
    arguments = invocation.arguments; 
    currentDir = invocation.currentDirectory; 
} 

Captura de argumentos de la línea de comandos

Los argumentos de la línea de comandos asociados con la invocación de una aplicación de AIR se transmiten en el objeto InvokeEvent distribuido por el objeto NativeApplication. La propiedad arguments de InvokeEvent contiene un conjunto de los argumentos que pasa el sistema operativo cuando se invoca una aplicación de AIR. Si los argumentos contienen rutas a archivos relacionados, normalmente se pueden resolver las rutas con la propiedad currentDirectory.

Los argumentos que se pasan a un programa de AIR se tratan como cadenas delimitadas por espacios en blanco, a menos que estén entre comillas dobles:

Argumentos

Array

tick tock

{tick,tock}

tick "tick tock"

{tick,tick tock}

"tick" “tock”

{tick,tock}

\"tick\" \"tock\"

{"tick","tock"}

La propiedad currentDirectory contiene un objeto File que representa el directorio desde el cual se inició la aplicación.

Cuando se invoca una aplicación porque se abre un archivo del tipo registrado por la aplicación, la ruta nativa al archivo se incluye como cadena en los argumentos de la línea de comandos. (La aplicación se encarga de abrir el archivo y realizarle la operación prevista). Asimismo, si una aplicación está programada para actualizarse ella misma (en lugar de depender de la interfaz de usuario de actualización de AIR estándar), la ruta nativa al archivo de AIR se incluye cuando el usuario hace doble clic en un archivo de AIR que contenga una aplicación con un ID de aplicación igual.

Se puede acceder al archivo con el método resolve() del objeto File currentDirectory:

if((invokeEvent.currentDirectory != null)&&(invokeEvent.arguments.length > 0)){ 
    dir = invokeEvent.currentDirectory; 
    fileToOpen = dir.resolvePath(invokeEvent.arguments[0]); 
}

También debe validar que un argumento es realmente una ruta a un archivo.

Ejemplo: Historial de eventos de invocación

El siguiente ejemplo muestra cómo registrar detectores para el evento invoke y cómo se controla este evento. En el ejemplo se registran todos los eventos de invocación recibios y se muestran el directorio y los argumentos de la línea de comandos actuales.

Ejemplo de ActionScript

package  
{ 
    import flash.display.Sprite; 
    import flash.events.InvokeEvent; 
    import flash.desktop.NativeApplication; 
    import flash.text.TextField; 
         
    public class InvokeEventLogExample extends Sprite 
    { 
        public var log:TextField; 
         
        public function InvokeEventLogExample() 
        { 
            log = new TextField(); 
            log.x = 15; 
            log.y = 15; 
            log.width = 520; 
            log.height = 370; 
            log.background = true; 
             
            addChild(log); 
 
            NativeApplication.nativeApplication.addEventListener(InvokeEvent.INVOKE, onInvoke); 
        } 
             
        public function onInvoke(invokeEvent:InvokeEvent):void 
        { 
            var now:String = new Date().toTimeString(); 
            logEvent("Invoke event received: " + now); 
                     
            if (invokeEvent.currentDirectory != null) 
            { 
                logEvent("Current directory=" + invokeEvent.currentDirectory.nativePath); 
            }  
            else  
            { 
                logEvent("--no directory information available--"); 
            } 
                     
            if (invokeEvent.arguments.length > 0) 
            { 
                logEvent("Arguments: " + invokeEvent.arguments.toString()); 
            }  
            else  
            { 
                logEvent("--no arguments--"); 
            } 
        } 
                 
        public function logEvent(entry:String):void  
        { 
            log.appendText(entry + "\n"); 
            trace(entry); 
        } 
    } 
} 

Ejemplo de Flex

<?xml version="1.0" encoding="utf-8"?> 
<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" 
    invoke="onInvoke(event)" title="Invocation Event Log"> 
    <mx:Script> 
    <![CDATA[ 
    import flash.events.InvokeEvent; 
    import flash.desktop.NativeApplication; 
 
    public function onInvoke(invokeEvent:InvokeEvent):void { 
        var now:String = new Date().toTimeString(); 
        logEvent("Invoke event received: " + now); 
                 
        if (invokeEvent.currentDirectory != null){ 
            logEvent("Current directory=" + invokeEvent.currentDirectory.nativePath); 
        } else { 
            logEvent("--no directory information available--"); 
        } 
                 
        if (invokeEvent.arguments.length > 0){ 
            logEvent("Arguments: " + invokeEvent.arguments.toString()); 
        } else { 
            logEvent("--no arguments--"); 
        } 
    } 
             
    public function logEvent(entry:String):void { 
        log.text += entry + "\n"; 
        trace(entry); 
    } 
    ]]> 
    </mx:Script> 
    <mx:TextArea id="log" width="100%" height="100%" editable="false" 
        valueCommit="log.verticalScrollPosition=log.textHeight;"/> 
</mx:WindowedApplication>

Invocación de una aplicación de AIR en el inicio de sesión de usuario

Para configurar una aplicación de AIR de modo que se inicie automáticamente cuando el usuario actual inicia sesión, establezca la propiedad startAtLogin en true. Una vez configurada de esta forma, la aplicación se iniciará automáticamente cada vez que el usuario inicie sesión. Continúa iniciándose al iniciar sesión hasta que se cambie la configuración a false, el usuario modifique manualmente la configuración a través del sistema operativo o se desinstale la aplicación. El inicio de una aplicación al iniciar sesión es una opción del motor de ejecución. Esta configuración solo se aplica al usuario actual. Para lograr configurar la propiedad startAtLogin en true la aplicación debe estar ya instalada. Si se configura esta propiedad sin estar instalada la aplicación (cuando se inicia con ADL, por ejemplo), se emite un error.

Nota: la aplicación no se inicia cuando arranca el ordenador, sino que lo hace al iniciar sesión el usuario.

Para determinar si una aplicación se ha iniciado automáticamente o como resultado de una acción de usuario, puede examinarse la propiedad reason del objeto InvokeEvent. Si la propiedad es igual a InvokeEventReason.LOGIN, la aplicación se inicia automáticamente. Para otras rutas de invocación, la propiedad reason se define del modo siguiente:

  • InvokeEventReason.NOTIFICATION (solo iOS) - La aplicación se ha invocado a través de APN. Para obtener más información sobre APN, consulte Uso de notificaciones push.

  • InvokeEventReason.OPEN_URL - La aplicación se ha invocado a través de otra aplicación o por el sistema.

  • InvokeEventReason.Standard - Todos los demás casos.

Para acceder a la propiedad reason, la aplicación debe especificar como destino AIR 1.5.1 o versión superior (estableciendo el valor de espacio de nombres concreto en el archivo descriptor de la aplicación).

La siguiente aplicación simplificada utiliza la propiedad reason de InvokeEvent para decidir cómo comportarse cuando se produce un evento invoke. Si la propiedad reason es "login", la aplicación permanece en segundo plano. De lo contrario, hace que la aplicación pueda verse. Una aplicación que utilice este patrón suele comenzar en el inicio de sesión de modo que puede llevar a cabo el procesamiento en segundo plano o el control de eventos y abre una ventana como respuesta a un evento invoke desencadenado por el usuario.

package { 
    import flash.desktop.InvokeEventReason; 
    import flash.desktop.NativeApplication; 
    import flash.display.Sprite; 
    import flash.events.InvokeEvent; 
 
    public class StartAtLogin extends Sprite 
    { 
        public function StartAtLogin() 
        { 
            try 
            { 
                NativeApplication.nativeApplication.startAtLogin = true; 
            } 
            catch ( e:Error ) 
            { 
                trace( "Cannot set startAtLogin:" + e.message ); 
            } 
             
            NativeApplication.nativeApplication.addEventListener( InvokeEvent.INVOKE, onInvoke ); 
        } 
                 
        private function onInvoke( event:InvokeEvent ):void 
        { 
            if( event.reason == InvokeEventReason.LOGIN ) 
            { 
                //do background processing... 
                trace( "Running in background..." ); 
            }             
            else 
            { 
                this.stage.nativeWindow.activate(); 
            } 
        } 
    } 
}
Nota: para ver la diferencia de comportamiento, empaquete e instale la aplicación. La propiedad startAtLogin solo se puede definir para aplicaciones instaladas.

Invocación de una aplicación de AIR desde el navegador

La función de invocación desde el navegador permite que un sitio web inicie una aplicación de AIR instalada desde el navegador. La invocación desde el navegador solo se admite si el archivo descriptor de la aplicación define allowBrowserInvocation en true:

<allowBrowserInvocation>true</allowBrowserInvocation>

Cuando se invoca la aplicación a través del navegador, el objeto NativeApplication de la aplicación distribuye un objeto BrowserInvokeEvent.

Para recibir eventos BrowserInvokeEvent, llame al método addEventListener() del objeto NativeApplication (NativeApplication.nativeApplication) en la aplicación de AIR. Cuando un detector de eventos se registra para un evento BrowserInvokeEvent, también recibe todos los eventos BrowserInvokeEvent que se produjeron antes de haberse registrado el detector. Estos eventos se distribuyen tras la devolución de la llamada a addEventListener(), pero no necesariamente antes de otros eventos BrowserInvokeEvent que quizá se reciban después de registrar el detector. Esto le permite controlar los eventos BrowserInvokeEvent que se hayan producido antes de ejecutarse el código de inicialización (por ejemplo, cuando la aplicación se invocó desde el navegador). Tenga en cuenta que si se añade un detector de eventos más adelante en la ejecución (después de inicializada la aplicación), aún recibirá todos los eventos BrowserInvokeEvent que se hayan producido desde que se inició la aplicación.

El objeto BrowserInvokeEvent incluye las siguientes propiedades:

Propiedad

Descripción

arguments

Un conjunto de argumentos (cadenas) que se pasan a la aplicación.

isHTTPS

Indica si el contenido en el navegador utiliza el esquema https de la URL (true) o no (false).

isUserEvent

Indica si la invocación desde el navegador produjo un evento de usuario (hacer clic, por ejemplo). En AIR 1.0 siempre está definido en true; AIR requiere un evento de usuario para la función de invocación desde el navegador.

sandboxType

El tipo de entorno limitado para el contenido en el navegador. Los valores válidos se definen igual que los que pueden utilizarse en la propiedad Security.sandboxType y pueden ser uno de los siguientes:

  • Security.APPLICATION: el contenido se encuentra en el entorno limitado de seguridad de la aplicación.

  • Security.LOCAL_TRUSTED: el contenido se encuentra en el entorno limitado de seguridad local de confianza.

  • Security.LOCAL_WITH_FILE: el contenido se encuentra en el entorno limitado de seguridad local con sistema de archivos.

  • Security.LOCAL_WITH_NETWORK: el contenido se encuentra en el entorno limitado de seguridad local de red.

  • Security.REMOTE: el contenido se encuentra en un dominio remoto (en la red).

securityDomain

El dominio de seguridad para el contenido del navegador, por ejemplo "www.adobe.com" o "www.example.org". Esta propiedad solo se define para contenido en el entorno limitado de seguridad remota (para contenido procedente de un dominio de la red). No se define para contenido en un entorno limitado de seguridad local o de la aplicación.

Si utiliza la función de invocación desde el navegador, asegúrese de tener en cuenta las posibles consecuencias para la seguridad. Cuando un sitio web inicia una aplicación de AIR, puede enviar datos a través de la propiedad arguments del objeto BrowserInvokeEvent. Tenga cuidado al utilizar estos datos en operaciones sensibles, como las API de carga de archivos o código. El nivel de riesgo dependerá de lo que la aplicación haga con los datos. Si se espera que un solo sitio web en particular invoque la aplicación, esta debería comprobar la propiedad securityDomain del objeto BrowserInvokeEvent. También se puede exigir que el sitio web que invoca la aplicación utilice HTTPS, lo cual se puede verificar comprobando la propiedad isHTTPS del objeto BrowserInvokeEvent.

La aplicación debe validar los datos que le llegan. Por ejemplo, si una aplicación espera recibir unas URL a un dominio concreto, debería validar que las URL apunten, efectivamente, a ese dominio. Esto puede evitar que un atacante logre engañar la aplicación para que esta le mande datos sensibles.

Ninguna aplicación debería utilizar argumentos BrowserInvokeEvent que puedan apuntar a recursos locales. Por ejemplo: una aplicación no debería crear objetos File basados en una ruta recibida del navegador. Si se espera recibir rutas remotas del navegador, la aplicación debería asegurarse de que las rutas no utilicen el protocolo file:// en lugar de un protocolo remoto.

Cierre de una aplicación

La forma más rápida de cerrar una aplicación es llamar al método NativeApplication exit(). Esto funciona perfectamente cuando la aplicación no tiene datos que guardar ni recursos externos que limpiar. Al llamar a exit() se cierran todas las ventanas y después la aplicación. No obstante, para permitir que las ventanas u otros componentes de la aplicación interrumpan el proceso de cierre, quizá para guardar datos esenciales, distribuya los eventos de aviso correspondientes antes de llamar a exit().

Otro aspecto que conviene tener en cuenta para cerrar correctamente una aplicación es la necesidad de proporcionar una sola ruta de ejecución, independientemente de cómo se inicia el proceso de cierre. El usuario (o el sistema operativo) puede activar el cierre de la aplicación de las siguientes maneras:

  • Cerrando la última ventana de la aplicación cuando NativeApplication.nativeApplication.autoExit está definido en true.

  • Seleccionando el comando de salir de la aplicación en el sistema operativo; por ejemplo, cuando el usuario selecciona en el menú predeterminado el comando de salir de la aplicación. (Esto solo sucede en Mac OS, ya que Windows y Linux no ofrecen un comando de salida de la aplicación en el fondo cromático del sistema).

  • Apagando el ordenador.

Cuando el comando de salir se ejecuta a través del sistema operativo por una de estas vías, el objeto NativeApplication distribuye un evento exiting. Si ningún detector cancela el evento exiting, se cierran las ventanas que estén abiertas. Cada ventana distribuye un evento closing seguido de un evento close. Si alguna de las ventanas cancela el evento closing, se detiene el proceso de cierre.

Si el orden de cierre de las ventanas es un problema para la aplicación, detecte el evento exiting distribuido por el objeto NativeApplication y cierre usted mismo las ventanas en el orden correcto. Este podría ser el caso si, por ejemplo, hay una ventana de documento con paletas de herramientas. Puede resultar inapropiado, o peor, si el sistema cerrara las paletas, pero el usuario decidiera cancelar el comando de salir para guardar datos. En Windows, la única vez que se produce el evento exiting es después de cerrar la última ventana (cuando la propiedad autoExit del objeto NativeApplication está definida en true).

Para obtener un comportamiento similar en todas las plataformas -independientemente de si la secuencia de cierre se inicia a través del fondo cromático del sistema operativo, los comandos de menú o la lógica de la aplicación- conviene observar las siguientes prácticas recomendadas para salir de la aplicación:

  1. Distribuya siempre un evento exiting a través del objeto NativeApplication antes de llamar a exit() en el código de la aplicación y compruebe que no vaya a cancelar el evento otro componente de la aplicación.

    public function applicationExit():void { 
        var exitingEvent:Event = new Event(Event.EXITING, false, true); 
        NativeApplication.nativeApplication.dispatchEvent(exitingEvent); 
        if (!exitingEvent.isDefaultPrevented()) { 
            NativeApplication.nativeApplication.exit(); 
        } 
    } 
  2. Esté atento al evento exiting distribuido por el objeto NativeApplication.nativeApplication y, en el controlador, cierre las ventanas abiertas que haya (distribuyendo primero un evento closing). Una vez cerradas todas las ventanas, realice las tareas de limpieza necesarias, como guardar los datos de la aplicación o eliminar los archivos temporales. Utilice solamente métodos sincrónicos durante la limpieza para estar seguro de que hayan finalizado antes de que se cierre la aplicación.

    Si no importa en qué orden se cierran las ventanas, se puede recorrer el conjunto NativeApplication.nativeApplication.openedWindows en un bucle y cerrar cada ventana a su vez. Si el orden importa, facilite un medio de cerrar las ventanas en la secuencia correcta.

    private function onExiting(exitingEvent:Event):void { 
        var winClosingEvent:Event; 
        for each (var win:NativeWindow in NativeApplication.nativeApplication.openedWindows) { 
            winClosingEvent = new Event(Event.CLOSING,false,true); 
            win.dispatchEvent(winClosingEvent); 
            if (!winClosingEvent.isDefaultPrevented()) { 
                win.close(); 
            } else { 
                exitingEvent.preventDefault(); 
            } 
        } 
         
        if (!exitingEvent.isDefaultPrevented()) { 
            //perform cleanup 
        } 
    } 
  3. Las ventanas deben siempre controlar su propia limpieza, detectando sus propios eventos closing.

  4. Utilice un solo detector de eventos exiting en la aplicación, ya que los controladores que se llamaron previamente no pueden saber si los detectores posteriores cancelarán el evento exiting (y no conviene depender del orden de ejecución).