2010-02-07 22 views
12

Adobe air runtime evita que se inicie al mismo tiempo más de una instancia de una aplicación de aire. ¿Es seguro eludir esta restricción cambiando arbitrariamente la ID del editor? ¿Alguien sabe si Adobe planea permitir múltiples instancias concurrentes en Air 2.0?iniciar una aplicación de Adobe AIR varias veces

+0

buenas respuestas por favor para ganar la recompensa. – abc

Respuesta

19

Implementamos con éxito un truco para eludir esta limitación, de forma puramente AIR, sin tener que cambiar la id del editor (que necesita varios certificados, creo).

Como ya sabe, AIR implementa su Mutex utilizando un identificador de aplicación único. Este identificador se calcula utilizando el ID de la aplicación y el identificador del editor (extraído del certificado que firmó la aplicación).

En el directorio de instalación de una aplicación de AIR, hay una carpeta META-INF (o en/share/using Linux). Esta carpeta META-INF contiene una carpeta AIR, que contiene un archivo "application.xml". Este archivo contiene una etiqueta <id /> que define el identificador de la aplicación, que se utiliza en el cálculo del identificador mutex. Si su aplicación puede escribir en la carpeta de instalación, puede usar la API File para editarla en tiempo de ejecución, cambiando aleatoriamente la etiqueta <id />, permitiendo que varios procesos de la misma aplicación se ejecuten al mismo tiempo.

Esto está teniendo algunos efectos secundarios molestos, como crear una nueva carpeta en la carpeta File.applicationStorageDirectory todo el tiempo. Pero utilizando un LocalConnection, puede minimizar esto reutilizando el mismo identificador varias veces al iniciar sesión, cuáles son libres de reutilización. Además, SharedObject se almacenan en esta carpeta, por lo que no se pueden usar (o tienen que copiarse cada vez que se crea una nueva instancia, y se sincronizan a través de LocalConnection).

Por lo que sé, Adobe no planea eliminar esta limitación nativa. Se implementó para propósitos de plataformas múltiples, específicamente en MacOS, donde el dock lo hace más complicado (no es muy fácil comenzar la misma aplicación dos veces con el dock).

La forma oficial de hacerlo es detectar el evento InvokeEvent.INVOKE y hacer cosas como abrir una nueva ventana. Y no hay cambios planeados para AIR 2.0 en este comportamiento.

1

¿Te ayudaría si encapsulas la lógica de tu aplicación como una clase que podría ejecutarse en una ventana y permitir al usuario crear varias instancias de esa ventana, dentro de una aplicación? ¿Eso ayudaría?

¿Cuál es la razón principal por la que necesitaría múltiples aplicaciones?

+0

Gracias por la respuesta, pero específicamente estoy buscando procesos separados. Hay una serie de beneficios, por ejemplo, ejecución paralela en computadoras multiprocesador. – abc

+0

Ya veo, gracias por la explicación. Buena pregunta. Puede tener procesos separados en Java ... esto podría ser demasiado largo, sería bueno hacer interfaz entre AIR y Java a través de Merapi (http://merapiproject.net/). –

+0

Nuevamente, publicación interesante, pero está fuera de tema. Merapi es un marco de IPC en lugar de un marco de enlace (como JNI), por lo que no es posible vincularlo a una biblioteca de Java multiproceso; tendría que haber un proceso Java por separado. Además, usar un java helper solo proporcionaría procesamiento paralelo, no otras ventajas de procesos múltiples. Volvamos a la pregunta original: estoy buscando una manera de iniciar varias instancias de una aplicación Flex AIR, y específicamente si es legal ejecutar el mismo swf con múltiples identificadores de editores en el tiempo de ejecución de AIR. – abc

0

Esto romperá la actualización automática, tenga cuidado.

1

Air Application duplicator le ayudará en esta parte. Al usar esto, puede ejecutar múltiples instancias de la misma aplicación de AIR.

https://github.com/chrisdeely/AirAppDuplicator

Su simplemente copiando el directorio de aplicación con el nuevo nombre y la nueva ID de aplicación.

0

Acabo de hacer una clase rápida para implementar la solución de Tyn. Simplemente llama. MultipleInstanceAirApp.changeMetaInfId(stage);

Realmente no necesita la parte de escenario, pero la utilizo para cambiar la posición de la ventana cuando la pruebo. De todos modos, ¡disfrútalo!

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

¡Bienvenido a Stack Overflow! Aunque este fragmento de código es bienvenido, y puede brindar cierta ayuda, sería [mejorado mucho si incluyera una explicación] (// meta.stackexchange.com/q/114762) de * cómo * aborda la pregunta. Sin eso, tu respuesta tiene mucho menos valor educativo: recuerda que estás respondiendo la pregunta a los lectores en el futuro, ¡no solo a la persona que pregunta ahora! Por favor [edite] su respuesta para agregar una explicación y dar una indicación de qué limitaciones y suposiciones se aplican. –

Cuestiones relacionadas