2010-02-07 15 views
12

Adobe AIR Runtime impedisce l'avvio simultaneo di più istanze di un'applicazione Air. È sicuro aggirare questa restrizione modificando arbitrariamente l'ID del publisher? Qualcuno sa se Adobe prevede di consentire più istanze concorrenti in Air 2.0?avvio di un'applicazione Adobe AIR più volte

+0

buone risposte si prega di guadagnare la taglia. – abc

risposta

19

Abbiamo implementato con successo un hack per aggirare questa limitazione, in puro modo AIR, senza dover modificare l'id del publisher (che necessita di più certificati, penso).

Come sapete, AIR sta implementando il suo Mutex utilizzando un identificatore di applicazione univoco. Questo identificatore viene calcolato utilizzando l'ID applicazione e l'identificatore del publisher (estratto dal certificato che ha firmato l'applicazione).

Nella directory di installazione di un'applicazione AIR, esiste una cartella META-INF (o in/share/using Linux). Questa cartella META-INF contiene una cartella AIR, che contiene un file "application.xml". Questo file contiene un tag <id /> che definisce l'identificativo dell'applicazione, utilizzato nel calcolo dell'identificatore mutex. Se l'applicazione può scrivere nella cartella di installazione, è possibile utilizzare l'API File per modificarla in fase di runtime, modificando in modo casuale il tag <id />, consentendo l'esecuzione contemporanea di più processi della stessa applicazione.

Questo sta avendo alcuni fastidiosi effetti collaterali, come la creazione di una nuova cartella nella cartella File.applicationStorageDirectory ogni volta. Ma usando lo LocalConnection, puoi minimizzare questo riutilizzando lo stesso identificatore più volte registrando quali sono liberi di essere riutilizzati. Inoltre, SharedObject sono memorizzati in questa cartella, quindi non possono essere utilizzati (o devono essere copiati ogni volta che viene creata una nuova istanza e sincronizzati tramite LocalConnection).

Per quanto ne so, Adobe non ha intenzione di rimuovere questa limitazione nativa. È stato implementato per scopi multipiattaforma, in particolare su MacOS, dove il dock lo rende più complicato (non è molto semplice avviare la stessa applicazione due volte con il dock).

Il modo ufficiale per farlo è catturare l'evento InvokeEvent.INVOKE e fare cose come aprire una nuova finestra. E non c'è nessun cambiamento previsto per AIR 2.0 in questo comportamento.

1

Sarebbe d'aiuto se incapsulassi la logica della tua applicazione come una classe che potesse essere eseguita in una finestra e consentire all'utente di creare più istanze di quella finestra, all'interno di un'app? sarebbe d'aiuto?

Qual è il motivo principale per cui sono necessarie più applicazioni?

+0

Grazie per la risposta, ma sono specificamente alla ricerca di processi separati. Esistono numerosi vantaggi, ad esempio l'esecuzione parallela su computer multiprocessore. – abc

+0

Vedo, grazie per la spiegazione. Buona domanda. È possibile avere processi separati in Java ... questo potrebbe essere un modo prolisso, l'interfacciamento tra AIR e Java tramite Merapi (http://merapiproject.net/) può essere utile? –

+0

Ancora, post interessante, ma è fuori tema. Merapi è un framework IPC piuttosto che un framework di collegamento (come JNI), quindi non è possibile collegarsi a una libreria java multi-thread; ci dovrebbe essere un processo java avviato separatamente. Inoltre, l'utilizzo di un helper java fornirebbe solo un'elaborazione parallela, non altri vantaggi di più processi. Torniamo alla domanda originale: sto cercando un modo per avviare diverse istanze di un'app Flex AIR, e in particolare se l'esecuzione dello stesso swf con più ID publisher è legale nel runtime AIR. – abc

0

Questo interromperà l'aggiornamento automatico, essere avvisati.

1

Il duplicatore di applicazioni aria vi aiuterà in questa parte. Usando questo puoi eseguire più istanze della stessa applicazione AIR.

https://github.com/chrisdeely/AirAppDuplicator

suo solo semplicemente copiare la directory app con nuovo nome e nuova applicazione id.

0

Ho appena fatto una lezione veloce per implementare la soluzione di Tyn. Basta chiamare. MultipleInstanceAirApp.changeMetaInfId(stage);

Non hai davvero bisogno della parte scenica, ma la uso per cambiare la posizione della finestra quando eseguo il test. Ad ogni modo, buon divertimento!

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

Benvenuto in Stack Overflow! Sebbene questo snippet di codice sia benvenuto e possa fornire un aiuto, sarebbe [notevolmente migliorato se includesse una spiegazione] (// meta.stackexchange.com/q/114762) di * come * affronta la domanda. Senza di ciò, la tua risposta ha un valore educativo molto inferiore - ricorda che stai rispondendo alla domanda per i lettori in futuro, non solo per chi chiede ora! Si prega di [modificare] la risposta per aggiungere una spiegazione e fornire un'indicazione di quali limitazioni e ipotesi si applicano. –

Problemi correlati