Appel et fermeture d’une application AIR

Adobe AIR 1.0 et les versions ultérieures

Cette section est consacrée aux différentes techniques d’appel d’une application Adobe® AIR® installée, ainsi qu’aux options et considérations de fermeture d’une application en cours d’exécution.

Remarque : les objets NativeApplication, InvokeEvent et BrowserInvokeEvent ne sont proposés qu’au contenu SWF qui s’exécute dans le sandbox d’une application AIR. Un contenu SWF qui s’exécute dans le moteur d’exécution Flash Player, au sein du navigateur ou du lecteur autonome (projection), ou dans une application AIR hors du sandbox d’application, ne peut pas accéder à ces classes.

Pour obtenir une explication rapide de l’ouverture et de la fermeture d’une application AIR et des exemples de code correspondants, voir les articles de démarrage rapide suivants dans Adobe Developer Connection :

Appel d’une application

Une application AIR est appelée lorsque l’utilisateur (ou le système d’exploitation) exécute l’une des actions suivantes :

  • Il lance l’application à partir du shell de poste de travail.

  • Il utilise l’application comme une commande sur un shell de ligne de commande.

  • Il ouvre un type de fichier pour lequel cette application correspond à l’application d’ouverture définie par défaut.

  • (Mac OS X) Il clique sur l’icône de l’application sur la barre des tâches du Dock (que l’application soit en cours d’exécution ou non).

  • Il choisit de lancer l’application à partir du programme d’installation (à la fin d’un nouveau processus d’installation ou après un double-clic sur le fichier AIR d’une application déjà installée).

  • Il commence une mise à jour d’une application AIR alors que la version installée l’informe qu’elle est déjà en train de traiter des mises à jour (par l’inclusion de la déclaration <customUpdateUI>true</customUpdateUI> dans le fichier descripteur d’application).

  • (iOS) Il reçoit une notification du service APN (Apple Push Notification).

  • Il invoque l’application via une URL.

  • Il consulte une page Web hébergeant une application ou un badge Flash appelant la méthode com.adobe.air.AIR launchApplication() qui spécifie les informations d’identification relatives à l’application AIR. (Le descripteur d’application doit également comprendre une déclaration <allowBrowserInvocation>true</allowBrowserInvocation> afin que les appels au navigateur aboutissent.)

Dès qu’une application AIR est appelée, AIR distribue un objet InvokeEvent de type invoke par le biais de l’objet NativeApplication Singleton. Afin de laisser à une application le temps de s’initialiser et d’enregistrer un écouteur d’événement, les événements invoke sont placés en file d’attente au lieu d’être ignorés. Dès qu’un écouteur est enregistré, tous les événements placés en file d’attente sont livrés.

Remarque : lorsqu’une application est appelée au moyen de la fonction d’appel du navigateur, l’objet NativeApplication distribue un événement invoke uniquement si l’application n’est pas en cours d’exécution.

Pour recevoir des événements invoke , appelez la méthode addEventListener() de l’objet NativeApplication ( NativeApplication.nativeApplication) . Lorsqu’un écouteur d’événement s’enregistre pour un événement invoke , il reçoit également tous les événements invoke survenus avant l’enregistrement. Les événements invoke placés en file d’attente sont distribués un par un dans un court laps de temps après le renvoi de l’appel à addEventListener() . Si un nouvel événement invoke se produit au cours de ce processus, il peut être distribué avant un ou plusieurs des événements placés en file d’attente. Ce placement des événements en file d’attente vous permet de gérer tous les événements invoke survenus avant l’exécution de votre code d’initialisation. Sachez que si, plus tard dans l’exécution (après l’initialisation de l’application), vous ajoutez un écouteur d’événement, celui-ci recevra toujours tous les événements invoke survenus depuis le lancement de l’application.

Une seule occurrence d’une application AIR est lancée. Lorsqu’une application en cours d’exécution est à nouveau appelée, AIR distribue un nouvel événement invoke vers elle. C’est l’application AIR qui est chargée de répondre à un événement invoke et de prendre les mesures appropriées (comme l’ouverture d’une nouvelle fenêtre de document).

Un objet InvokeEvent contient tous les arguments transmis à l’application, de même qu’un répertoire à partir duquel l’application a été appelée. Si l’application a été appelée via une association de type de fichier, le chemin d’accès complet au fichier est alors inclus dans les arguments de ligne de commande. De la même manière, si l’application a été appelée via une mise à jour, le chemin d’accès complet au fichier AIR de mise à jour est fourni.

Lorsque plusieurs fichiers sont ouverts simultanément, un seul objet InvokeEvent est distribué sous Mac OS X. Chaque fichier est inclus dans le tableau arguments . Sous Windows et Linux, un objet InvokeEvent distinct est distribué pour chaque fichier.

Votre application gère les événements invoke en enregistrant un écouteur avec son objet NativeApplication :

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

Et en définissant un écouteur d’événement :

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

Capture des arguments de ligne de commande

Les arguments de ligne de commande associés à l’appel d’une application AIR sont livrés dans l’objet InvokeEvent distribué par l’objet NativeApplication. La propriété arguments d’un objet InvokeEvent contient un tableau des arguments transmis par le système d’exploitation suite à l’appel d’une application AIR. Si les arguments contiennent des chemins de fichiers relatifs, il est généralement possible de résoudre les chemins à l’aide de la propriété currentDirectory .

Les arguments transmis à un programme AIR sont traités sous forme de chaînes délimitées par des espaces, à moins d’être placés entre guillemets doubles :

Arguments

Array

tick tock

{tick,tock}

tick "tick tock"

{tick,tick tock}

"tick" “tock”

{tick,tock}

\"tick\" \"tock\"

{"tick","tock"}

La propriété currentDirectory d’un objet InvokeEvent contient un objet File représentant le répertoire à partir duquel l’application a été lancée.

Lorsqu’une application est appelée suite à l’ouverture d’un fichier dont le type est enregistré par l’application, le chemin d’accès natif au fichier est compris dans les arguments de ligne de commande sous forme de chaîne. (Votre application est chargée d’ouvrir le fichier ou d’effectuer l’opération attendue.) De la même manière, lorsqu’une application est programmée pour se mettre à jour automatiquement (plutôt que de dépendre de l’interface utilisateur de mise à jour d’AIR standard), le chemin d’accès natif au fichier AIR est inclus si un utilisateur double-clique sur un fichier AIR contenant une application dotée de l’ID d’application correspondant.

Vous pouvez accéder au fichier à l’aide de la méthode resolve() de l’objet File currentDirectory :

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

Il est également recommandé de vérifier qu’un argument correspond effectivement à un chemin d’accès à un fichier.

Exemple : Journal des événements d’appel

L’exemple suivant explique comment enregistrer des écouteurs pour l’événement invoke et comment gérer ce type d’événement. Dans cet exemple, tous les événements d’appel reçus sont consignés dans un journal et le répertoire actif ainsi que les arguments de ligne de commande sont affichés.

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

Exemple 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>

Appel d’une application AIR lors de la connexion d’un utilisateur

Il est possible de configurer le lancement automatique d’une application AIR au moment de la connexion de l’utilisateur actif en définissant la propriété NativeApplication startAtLogin sur true . Une fois ce paramètre défini, l’application est lancée automatiquement chaque fois que l’utilisateur se connecte. Elle continue à démarrer lors d’une connexion tant que le paramètre n’est pas défini sur false . Soit ce changement est effectué manuellement par l’utilisateur via le système d’exploitation, soit il survient suite à la désinstallation de l’application. Le lancement au moment de la connexion est un paramètre d’exécution. Le paramètre s’applique uniquement à l’utilisateur actif. L’application doit être installée pour que la propriété startAtLogin soit définie sur true . Une erreur est renvoyée si la propriété est définie alors que l’application n’est pas installée (suite à un lancement à l’aide d’ADL, par exemple).

Remarque : l’application n’est pas lancée au démarrage du système informatique. Elle s’ouvre lorsque l’utilisateur se connecte.

Pour déterminer si une application a démarré automatiquement ou si son lancement résulte d’une action utilisateur, vous pouvez examiner la propriété reason de l’objet InvokeEvent. Si la propriété est définie sur InvokeEventReason.LOGIN , l’application a démarré automatiquement. Pour d’autres chemins d’invocation, la propriété reason est définie comme suit :

  • InvokeEventReason.NOTIFICATION (iOS uniquement) : l’application a été invoquée via le service APN. Pour plus d’informations sur le service APN, voir Utilisation de notifications Push .

  • InvokeEventReason.OPEN_URL : l’application a été invoquée par une autre application ou par le système.

  • InvokeEventReason.Standard : tous les autres cas.

Pour accéder à la propriété reason , votre application doit cibler AIR 1.5.1 ou une version ultérieure (pour ce faire, définissez la valeur d’espace de noms correcte dans le fichier descripteur de l’application).

L’application simplifiée suivante fait appel à la propriété reason de l’objet InvokeEvent pour déterminer son comportement lorsqu’il se produit un événement invoke. Si la propriété reason est définie sur « login », l’application demeure en arrière-plan. Si tel n’est pas le cas, l’application principale devient visible. Une application qui fait appel à cette technique démarre généralement lorsque l’utilisateur se connecte pour pouvoir exécuter un traitement en arrière-plan ou une surveillance d’événement et ouvre une fenêtre en réponse à un événement déclenché par l’utilisateur.

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(); 
            } 
        } 
    } 
}
Remarque : pour évaluer la différence de comportement, mettez en package l’application et installez-la. La propriété startAtLogin est réservée aux applications installées.

Appel d’une application AIR à partir du navigateur

La fonction d’appel du navigateur permet à un site Web de lancer une application AIR installée à partir du navigateur. L’appel du navigateur est uniquement autorisé si le fichier descripteur d’application définit allowBrowserInvocation sur true :

<allowBrowserInvocation>true</allowBrowserInvocation>

Lorsque l’application est appelée par le biais du navigateur, l’objet NativeApplication de l’application distribue un objet BrowserInvokeEvent.

Pour recevoir des événements BrowserInvokeEvent, appelez la méthode addEventListener() de l’objet NativeApplication ( NativeApplication.nativeApplication ) dans l’application AIR. Lorsqu’un écouteur d’événement s’enregistre pour un événement BrowserInvokeEvent, il reçoit également tous les événements BrowserInvokeEvent survenus avant l’enregistrement. Ces événements sont distribués après le renvoi de l’appel à addEventListener() , mais pas nécessairement avant d’autres événements BrowserInvokeEvent susceptibles d’être reçus après l’enregistrement. Cela vous permet de gérer les événements BrowserInvokeEvent survenus avant l’exécution de votre code d’initialisation (comme, par exemple, lorsque l’application a été appelée au départ depuis le navigateur). Sachez que si, plus tard dans l’exécution (après l’initialisation de l’application), vous ajoutez un écouteur d’événement, celui-ci recevra toujours tous les événements BrowserInvokeEvent survenus depuis le lancement de l’application.

L’objet BrowserInvokeEvent comprend les propriétés suivantes :

Propriété

Description

arguments

Tableau d’arguments (de chaînes) à transmettre à l’application.

isHTTPS

Indique si le contenu du navigateur utilise le modèle d’URL https ( true ) ou pas ( false ).

isUserEvent

Indique si l’appel du navigateur a entraîné un événement utilisateur (tel qu’un clic de souris). Dans AIR 1.0, cette propriété est toujours définie sur true ; AIR requiert un événement utilisateur pour la fonction d’appel du navigateur.

sandboxType

Type de sandbox relatif au contenu du navigateur. Les valeurs valides sont identiques à celles qui sont admises pour la propriété Security.sandboxType . Il peut s’agir de l’une des valeurs suivantes :

  • Security.APPLICATION : le contenu se trouve dans le sandbox de sécurité de l’application.

  • Security.LOCAL_TRUSTED : le contenu se trouve dans le sandbox de sécurité local avec système de fichiers.

  • Security.LOCAL_WITH_FILE : le contenu se trouve dans le sandbox de sécurité local avec système de fichiers.

  • Security.LOCAL_WITH_NETWORK : le contenu se trouve dans le sandbox de sécurité local avec accès au réseau.

  • Security.REMOTE : le contenu se trouve dans un domaine (réseau) distant.

securityDomain

Correspond au domaine de sécurité du contenu du navigateur, tel www.adobe.com ou www.example.org . Cette propriété est uniquement définie pour le contenu du sandbox de sécurité distant (pour le contenu d’un domaine réseau). Elle n’est pas définie pour un contenu figurant dans un sandbox de sécurité d’application ou local.

Si vous utilisez la fonction d’appel du navigateur, tenez compte des implications au niveau de la sécurité. Lorsqu’un site Web lance une application AIR, il peut envoyer les données par le biais de la propriété arguments de l’objet BrowserInvokeEvent. Utilisez ces données avec précaution dans toute opération délicate, telle que des API de chargement de code ou de fichier. Le niveau de risque varie en fonction de l’usage que l’application réserve aux données. Si seul un site Web spécifique doit appeler l’application, celle-ci doit vérifier la propriété securityDomain de l’objet BrowserInvokeEvent. Vous pouvez également exiger de la part du site Web appelant l’application qu’il utilise le protocole HTTPS, ce que vous pouvez contrôler en vérifiant la propriété isHTTPS de l’objet BrowserInvokeEvent.

L’application devrait valider les données transmises. Si, par exemple, une application s’attend à recevoir des URL pointant vers un domaine spécifique, elle devrait vérifier que les URL pointent réellement vers le domaine attendu. Cette procédure peut empêcher un pirate de tromper l’application en lui demandant de lui envoyer des données sensibles.

Aucune application ne devrait utiliser d’arguments BrowserInvokeEvent susceptibles de pointer vers des ressources locales. Ainsi, il est vivement déconseillé de concevoir une application qui crée des objets File à partir d’un chemin transmis depuis le navigateur. Si le navigateur peut transmettre des chemins distants, l’application devrait vérifier que ces derniers n’utilisent pas le protocole file:// à la place d’un protocole distant.

Fermeture d’une application

Le moyen le plus rapide pour fermer une application consiste à appeler la méthode NativeApplication exit(). Cette approche fonctionne correctement si votre application ne doit pas enregistrer de données ou nettoyer une ressource externe. L’appel de la méthode exit() entraîne la fermeture de toutes les fenêtres, puis celle de l’application. Toutefois, pour permettre aux fenêtres ou à d’autres composants de l’application d’interrompre le processus de fermeture, afin d’enregistrer des données cruciales par exemple, distribuez les événements d’avertissement pertinents avant d’appeler exit() .

Un autre point à prendre en compte dans le cadre de l’arrêt progressif d’une application est l’utilisation d’un seul chemin d’exécution, quelle que soit la manière dont le processus d’arrêt commence. L’utilisateur (ou le système d’exploitation) peut déclencher la fermeture de l’application de l’une des manières suivantes :

  • En fermant la dernière fenêtre de l’application lorsque la méthode NativeApplication.nativeApplication.autoExit est définie sur true .

  • En sélectionnant la commande de fermeture d’application à partir du système d’exploitation comme, par exemple, lorsque l’utilisateur choisit la commande Quitter de l’application dans le menu par défaut. (Cela se produit uniquement sous Mac OS, car Windows et Linux ne fournissent pas de commande de fermeture d’application via le chrome système.)

  • En arrêtant l’ordinateur.

Lorsqu’une commande de fermeture est traitée par le biais du système d’exploitation selon l’une de ces méthodes, NativeApplication distribue un événement exiting . Si aucun écouteur n’annule l’événement exiting , toutes les fenêtres qui étaient ouvertes se ferment. Chaque fenêtre distribue un événement closing suivi d’un événement close . Si l’une des fenêtres annule l’événement closing , le processus d’arrêt est interrompu.

Si l’ordre de fermeture des fenêtres présente un problème pour votre application, écoutez l’événement exiting de NativeApplication et fermez manuellement les fenêtres dans l’ordre approprié. Tel est par exemple le cas lorsqu’une fenêtre de document contient des palettes d’outils. Il peut s’avérer peu pratique (voire pire) que le système ferme les palettes alors que l’utilisateur a choisi d’annuler la commande de fermeture afin d’enregistrer des données. Sous Windows, vous obtiendrez uniquement l’événement exiting après la fermeture de la dernière fenêtre (lorsque la propriété autoExit de l’objet NativeApplication est définie sur true ).

Pour adopter un comportement homogène sur toutes les plates-formes, que la séquence de fermeture soit lancée via le chrome du système d’exploitation, les commandes de menu ou la logique de l’application, suivez les recommandations ci-après pour fermer l’application :

  1. Distribuez toujours un événement exiting par le biais de l’objet NativeApplication avant d’appeler exit() dans le code de l’application et vérifiez qu’aucun autre composant de l’application n’annule l’événement.

    public function applicationExit():void { 
        var exitingEvent:Event = new Event(Event.EXITING, false, true); 
        NativeApplication.nativeApplication.dispatchEvent(exitingEvent); 
        if (!exitingEvent.isDefaultPrevented()) { 
            NativeApplication.nativeApplication.exit(); 
        } 
    } 
  2. Ecoutez l’événement exiting de l’application à partir de l’objet NativeApplication.nativeApplication et, dans le gestionnaire, fermez toutes les fenêtres ouvertes (en distribuant d’abord un événement closing ). Effectuez les éventuelles tâches de nettoyage nécessaires (enregistrement des données de l’application, suppression des fichiers temporaires, etc.) une fois toutes les fenêtres fermées. Faites exclusivement appel à des méthodes synchrones lors du nettoyage afin de vous assurer qu’elles sont bien terminées avant la fermeture de l’application.

    Si l’ordre de fermeture des fenêtres est sans importance, vous pouvez analyser en boucle le tableau NativeApplication.nativeApplication.openedWindows et fermer chaque fenêtre une après l’autre. Si l’ordre de fermeture compte , élaborez un moyen de l’appliquer aux fenêtres.

    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. Les fenêtres devraient toujours gérer leur propre nettoyage en écoutant les événements closing qui les concernent.

  4. Utilisez un seul écouteur exiting dans l’application, car les gestionnaires appelés auparavant ignorent si des gestionnaires ultérieurs annuleront l’événement exiting (sans compter qu’il serait déraisonnable de se fier à l’ordre d’exécution).