2010-02-07 5 views
12

Adobe Air Runtime verhindert, dass mehr als eine Instanz einer Luftanwendung gleichzeitig gestartet wird. Ist es sicher, diese Einschränkung zu umgehen, indem Sie die Publisher-ID beliebig ändern? Weiß jemand, ob Adobe mehrere gleichzeitige Instanzen in Air 2.0 zulässt?Starten einer Adobe AIR-Anwendung mehrmals

+0

Gute Antworten bitte die Prämie zu verdienen. – abc

Antwort

19

Wir haben erfolgreich einen Hack implementiert, um diese Einschränkung auf eine reine AIR-Weise zu umgehen, ohne die Herausgeber-ID ändern zu müssen (die mehrere Zertifikate benötigt, glaube ich).

Wie Sie wissen, implementiert AIR seinen Mutex mithilfe einer eindeutigen Anwendungskennung. Dieser Bezeichner wird mithilfe der Anwendungs-ID und der Herausgeber-ID berechnet (extrahiert aus dem Zertifikat, mit dem die Anwendung signiert wurde).

Im Installationsverzeichnis einer AIR-Anwendung befindet sich ein META-INF-Ordner (oder in/share/unter Linux). Dieser META-INF-Ordner enthält einen AIR-Ordner, der eine Datei "application.xml" enthält. Diese Datei enthält ein <id />-Tag, das die Anwendungs-ID definiert, die bei der Berechnung der Mutex-ID verwendet wird. Wenn Ihre Anwendung im Installationsordner schreiben kann, können Sie sie mit der API File zur Laufzeit bearbeiten, indem Sie das Tag <id /> zufällig ändern, sodass mehrere Prozesse derselben Anwendung gleichzeitig ausgeführt werden können.

Dies hat einige lästige Nebenwirkungen, wie das Erstellen eines neuen Ordners im Ordner File.applicationStorageDirectory jedes Mal. Wenn Sie jedoch LocalConnection verwenden, können Sie dies minimieren, indem Sie den gleichen Bezeichner mehrmals wiederverwenden, indem Sie protokollieren, welche für die erneute Verwendung frei sind. Außerdem sind SharedObject in diesem Ordner gespeichert und können daher nicht verwendet werden (oder müssen jedes Mal kopiert werden, wenn eine neue Instanz erstellt und synchronisiert wird, obwohl LocalConnection).

Soweit ich weiß, plant Adobe nicht, diese native Beschränkung zu entfernen. Es wurde für Multi-Plattform-Zwecke implementiert, speziell auf MacOS, wo das Dock dies komplizierter macht (es ist nicht sehr einfach, dieselbe Anwendung zweimal mit dem Dock zu starten).

Der offizielle Weg, um das zu tun, ist das InvokeEvent.INVOKE Ereignis zu fangen, und Dinge wie das Öffnen eines neuen Fensters zu tun. In diesem Verhalten ist keine Änderung für AIR 2.0 geplant.

1

Wäre es hilfreich, wenn Sie die Logik Ihrer Anwendung als eine Klasse kapseln, die in einem Fenster ausgeführt werden kann und es dem Benutzer ermöglicht, mehrere Instanzen dieses Fensters innerhalb einer App zu erstellen? Würde das helfen?

Was ist der Hauptgrund, warum Sie mehrere Anwendungen benötigen?

+0

Danke für die Antwort, aber ich suche speziell nach getrennten Prozessen. Es gibt eine Reihe von Vorteilen, z. B. die parallele Ausführung auf Multiprozessor-Computern. – abc

+0

Ich sehe, danke für die Erklärung. Gute Frage. Sie können separate Prozesse in Java haben ... das könnte zu langatmig sein, würde die Anbindung von AIR und Java über Merapi (http://merapiproject.net/) etwas nützen? –

+0

Wieder, interessanter Beitrag, aber es ist off Thema. Merapi ist ein IPC-Framework und kein Link-Framework (wie JNI). Daher ist es nicht möglich, eine Verbindung zu einer Multithread-Java-Bibliothek herzustellen. es müsste einen separat gestarteten Java-Prozess geben. Außerdem würde die Verwendung eines Java-Helfers nur eine parallele Verarbeitung bereitstellen, nicht andere Vorteile von mehreren Prozessen. Lassen Sie uns zur ursprünglichen Frage zurückkehren: Ich suche nach einer Möglichkeit, mehrere Instanzen einer Flex AIR-App zu starten, und insbesondere, ob die Ausführung desselben Swf mit mehreren Publisher-IDs in der AIR-Laufzeit zulässig ist. – abc

0

Dies wird die automatische Aktualisierung unterbrechen, gewarnt werden.

1

Air Anwendung Duplikator wird Ihnen in diesem Teil helfen. Damit können Sie mehrere Instanzen derselben AIR-Anwendung ausführen.

https://github.com/chrisdeely/AirAppDuplicator

Es ist nur einfach kopieren Sie Ihre App-Verzeichnis mit neuem Namen und neuen Anwendungs-ID.

0

Nur eine kurze Klasse, um die Lösung von Tyn zu implementieren. Einfach anrufen. MultipleInstanceAirApp.changeMetaInfId(stage);

Sie brauchen nicht wirklich den Bühnenteil, aber ich benutze es, um die Position des Fensters zu ändern, wenn ich es teste. Wie auch immer, viel Spaß!

import flash.display.Stage; 
import flash.events.Event; 
import flash.events.IOErrorEvent; 
import flash.filesystem.File; 
import flash.filesystem.FileMode; 
import flash.filesystem.FileStream; 
import flash.utils.ByteArray; 

/** 
* @author Lachhh 
*/ 
public class MultipleInstanceAirApp { 
    static private var loadFile : File; 
    static private var thePath:String = "./META-INF/AIR/application.xml"; 
    static private var myGameId:String = "YOUR_GAME_ID"; 
    static private var stage:Stage ; 
    static private var metaInfString:String ; 

    static public var instanceNumber:int = 0; 

    static public function changeMetaInfId(pStage:Stage):void { 
     stage = pStage; 
     var path:String = File.applicationDirectory.resolvePath(thePath).nativePath; 
     loadFile = new File(path); 

     loadFile.addEventListener(Event.COMPLETE, onLoadMetaInf); 
     loadFile.addEventListener(IOErrorEvent.IO_ERROR, onIoError); 
     loadFile.load(); 
    } 

    private static function onLoadMetaInf(event : Event) : void { 
     loadFile.removeEventListener(Event.COMPLETE, onLoadMetaInf); 
     metaInfString = loadFile.data.toString(); 
     replaceMetaInfIdIfFound(); 
     saveStringToMetaInf(metaInfString); 
    } 

    static public function saveStringToMetaInf(s:String):void { 
     var b:ByteArray = new ByteArray(); 
     b.writeUTFBytes(s); 
     saveFile(b); 
    } 

    static public function saveFile(data:ByteArray):void { 
     var thePath:String = File.applicationDirectory.resolvePath(thePath).nativePath;  
     var saveFile:File = new File(thePath); 
     var fileStream:FileStream = new FileStream(); 
     fileStream.openAsync(saveFile, FileMode.WRITE); 
     fileStream.writeBytes(data); 
     fileStream.addEventListener(Event.CLOSE, onClose); 
     fileStream.close(); 
    } 

    static private function replaceMetaInfIdIfFound():void { 
     if(checkToReplaceId(1, 2)) return ; 
     if(checkToReplaceId(2, 3)) return ; 
     if(checkToReplaceId(3, 4)) return ; 
     checkToReplaceId(4, 1); 

    } 

    static private function checkToReplaceId(i:int, newI:int):Boolean { 
     var id:String = getGameIdWithBrackets(i); 
     var newId:String = getGameIdWithBrackets(newI); 
     if(metaInfString.indexOf(id) != -1) { 
      metaInfString = myReplace(metaInfString, id, newId); 
      instanceNumber = newI; 
      return true; 
     } 
     return false; 
    } 

    private static function onClose(event : Event) : void { 
     trace("all done!"); 
     placeScreenAccordingToInstanceNumber(); 
    } 

    static private function placeScreenAccordingToInstanceNumber():void {; 
     switch(instanceNumber) { 
      case 1 : 
       stage.nativeWindow.x = 115; 
       stage.nativeWindow.y = 37; 
       break; 
      case 2 : 
       stage.nativeWindow.x = 115 + 660; 
       stage.nativeWindow.y = 37; 
       break; 
      case 3 : 
       stage.nativeWindow.x = 115; 
       stage.nativeWindow.y = 37 + 380; 
       break; 
      case 4 : 
       stage.nativeWindow.x = 115 + 660; 
       stage.nativeWindow.y = 37 + 380; 
       break; 
     } 
    } 

    private static function onIoError(event : IOErrorEvent) : void { 
     trace("io Error"); 
    } 

    static private function getGameIdOriginalWithBrackets():String { 
     return "<id>" + myGameId + "</id>"; 
    } 

    static private function getGameIdWithBrackets(i:int):String { 
     if(i == 1) return getGameIdOriginalWithBrackets(); 
     return "<id>" + myGameId + i + "</id>"; 
    } 

    static public function myReplace(msg:String, toFind:String, toBeReplacedWith:String):String { 
     return msg.split(toFind).join(toBeReplacedWith) ; 
    } 
} 
0
package hobis.airpc 
{ 
    import flash.events.Event; 
    import flash.filesystem.File; 
    import flash.filesystem.FileMode; 
    import flash.filesystem.FileStream; 
    import flash.utils.ByteArray; 
    import jhb0b.utils.MArrayUtil; 

    public final class MAppXmlUpdateCounter 
    { 
     private static var _AppXmlFile:File; 

     public static function Update():void 
     { 
      _AppXmlFile = new File(File.applicationDirectory.nativePath);   
      _AppXmlFile = _AppXmlFile.resolvePath('META-INF\\AIR\\application.xml'); 
      _AppXmlFile.addEventListener(Event.COMPLETE, ppOpened); 
      _AppXmlFile.load();   
     } 

     private static function ppOpened(evt:Event):void 
     { 
      const trx1:RegExp = /<id>[\s\S]*?<\/id>/; 
      const trx2:RegExp = /<([^>]+)>/g; 

      var tXmlStr:String = _AppXmlFile.data.toString(); 
      var tMatArr:Array = tXmlStr.match(trx1); 
      if (!MArrayUtil.is_empty(tMatArr)) 
      { 
       var tIdTagStr:String = tMatArr[0]; 
       var tIdValStr:String = tIdTagStr.replace(trx2, ''); 

       var tOriVal:String; 
       var tNumVal:uint; 
       var tStrArr:Array = tIdValStr.split('-'); 
       if (tStrArr != null) 
       { 
        if (tStrArr.length == 2) 
        { 
         tOriVal = tStrArr[0]; 
         tNumVal = int(tStrArr[1]);        
        } 
        else 
        if (tStrArr.length == 1) 
        { 
         tOriVal = tStrArr[0]; 
         tNumVal = 0; 
        } 
        tNumVal++; 

        var tIdNewStr:String = '<id>' + tOriVal + '-' + tNumVal + '<\/id>';     
        var tNewXmlStr:String = tXmlStr.replace(tIdTagStr, tIdNewStr);     
        ppSaveFile(tNewXmlStr); 
       } 
      } 
      _AppXmlFile = null; 
     } 

     private static function ppSaveFile(val:String):void 
     { 
      var tfs:FileStream; 
      try 
      { 
       tfs = new FileStream(); 
       tfs.openAsync(_AppXmlFile, FileMode.WRITE); 
       var tba:ByteArray = new ByteArray(); 
       tba.writeUTFBytes(val);   
       tfs.writeBytes(tba);     
       tba.clear(); 
      } 
      catch (e:Error) { } 
      try 
      { 
       tfs.close(); 
      } 
      catch (e:Error) { } 
     } 
    } 
} 
+0

Willkommen bei Stack Overflow! Während dieses Code-Snippet willkommen ist und einige Hilfe bieten könnte, wäre es [stark verbessert, wenn es eine Erklärung enthielt] (// meta.stackexchange.com/q/114762) von * wie * es die Frage anspricht. Ohne dies hat Ihre Antwort viel weniger pädagogischen Wert - denken Sie daran, dass Sie die Frage für Leser in der Zukunft beantworten, nicht nur die Person, die jetzt fragt! Bitte [bearbeiten] Sie Ihre Antwort, um eine Erläuterung hinzuzufügen und geben Sie an, welche Einschränkungen und Annahmen gelten. –