基本範例如下:
/Main.as,會被編譯成 Main.swf
package
{
import external.comp.MyComp;
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
public class Main extends Sprite
{
private var _ldr:Loader;
public function Main()
{
trace(this, this.name);
_ldr = new Loader();
_ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderCompleteHandler);
_ldr.load(new URLRequest("External.swf"));
addChild(_ldr);
}
private function onLoaderCompleteHandler(e:Event):void{
_ldr.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoaderCompleteHandler);
var external:DisplayObject = _ldr.content;
var returnValue:String;
//call External.swf methods
external["doJob1"]();
returnValue = external["doJob2"]("Ben");
trace(this, this.name, "onLoaderCompleteHandler()", "doJob2() return value=" + returnValue);
//create instance
var MyCompCls:Class = _ldr.contentLoaderInfo.applicationDomain.getDefinition("external.comp.MyComp") as Class;
var myComp:Sprite = new MyCompCls() as Sprite;
this.addChild(myComp);
//call instance methods
myComp["doJob1"]();
returnValue = myComp["doJob2"]("Ben");
trace(this, this.name, "onLoaderCompleteHandler()", "instance.doJob2() return value=" + returnValue);
}
}
}
/External.as,會被編譯成 External.swf
package
{
import external.comp.MyComp;
import flash.display.Sprite;
import flash.events.Event;
public class External extends Sprite
{
public function External()
{
trace(this, this.name);
new MyComp;
this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void{
this.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
trace(this, this.name, "onAddedToStage()");
}
public function doJob1():void{
trace(this, this.name, "doJob1()");
}
public function doJob2(name:String):String{
var resp:String = "Hello, " + name;
trace(this, this.name, "doJob2()", "resp=" + resp);
return resp;
}
}
}
/external/comp/MyComp.as,會被 External.swf 所包含
package external.comp
{
import flash.display.Sprite;
import flash.events.Event;
public class MyComp extends Sprite
{
public function MyComp()
{
trace(this, this.name);
this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void{
this.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
trace(this, this.name, "onAddedToStage()");
}
public function doJob1():void{
trace(this, this.name, "doJob1()");
}
public function doJob2(name:String):String{
var resp:String = "Hello, " + name;
trace(this, this.name, "doJob2()", "resp=" + resp);
return resp;
}
}
}
執行結果如下:
[object Main] root1 [object External] instance2 [object MyComp] instance3 [object External] instance2 onAddedToStage() [object External] instance2 doJob1() [object External] instance2 doJob2() resp=Hello, Ben [object Main] root1 onLoaderCompleteHandler() doJob2() return value=Hello, Ben [object MyComp] instance4 [object MyComp] instance4 onAddedToStage() [object MyComp] instance4 doJob1() [object MyComp] instance4 doJob2() resp=Hello, Ben [object Main] root1 onLoaderCompleteHandler() instance.doJob2() return value=Hello, Ben
但是,如果你的專案需要使用 SWF Obfuscator 混淆過的話,譬如執行了以下 command line:
cd C:\works\MainExternalInteractiveDemo "C:\Program Files\secureSWF_v3pro_win_build5762\ssCLI.exe" ./bin-debug/Main.swf ./bin-debug -p:most_aggressive -v:10 "C:\Program Files\secureSWF_v3pro_win_build5762\ssCLI.exe" ./bin-debug/External.swf ./bin-debug -p:most_aggressive -v:10 pause
再重新執行的話,就會遇到 flash player error 了:
[object Main] root1 [object External] instance2 [object do] instance3 [object External] instance2 onAddedToStage() ReferenceError: Error #1069: External 上找不到屬性 doJob1,而且沒有預設值。 at Main/continue()[C:\works\MainExternalInteractiveDemo\src\Main.as:32]
因此,這裡提供第二種做法,
/Main2.as,會編譯成 Main2.swf
package
{
import external.comp.MyComp;
import flash.display.DisplayObject;
import flash.display.Loader;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.utils.describeType;
public class Main2 extends Sprite
{
private var _ldr:Loader;
public function Main2()
{
trace(this, this.name);
_ldr = new Loader();
_ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoaderCompleteHandler);
_ldr.load(new URLRequest("External2.swf"));
addChild(_ldr);
}
private function onLoaderCompleteHandler(e:Event):void{
_ldr.contentLoaderInfo.removeEventListener(Event.COMPLETE, onLoaderCompleteHandler);
var external:DisplayObject = _ldr.content;
var returnValue:String;
//call External.swf methods
methodCall(external, "job1");
returnValue = methodCall(external, "job2", ["Ben"]);
trace(this, this.name, "onLoaderCompleteHandler()", "doJob2() return value=" + returnValue);
var myCompClassName:String = methodCall(external, "getMyCompClassName");
trace(this, this.name, "onLoaderCompleteHandler()", "getMyCompClassName() return value=" + myCompClassName);
//create instance
var MyCompCls:Class = _ldr.contentLoaderInfo.applicationDomain.getDefinition(myCompClassName) as Class;
var myComp:Sprite = new MyCompCls() as Sprite;
this.addChild(myComp);
//call instance methods
methodCall(myComp, "job1");
returnValue = methodCall(myComp, "job2", ["Ben"]);
trace(this, this.name, "onLoaderCompleteHandler()", "instance.doJob2() return value=" + returnValue);
}
private function methodCall(owner:Object, methodName:String, args:Array=null):*{
trace(this, this.name, "methodCall()", "owner=" + owner, "methodName=" + methodName, "args=" + args);
var xml:XML = describeType(owner);
var find:XMLList = xml["method"]["metadata"].(@name=="PublicMethod")["arg"].(@key=="name");
for(var i:int=0; i<find.length(); i++){
if(find[i]["@value"] == methodName){
var realMethodName:String = XML(XML(XML(find[i]).parent()).parent())["@name"];
var returnValue:* = owner[realMethodName].apply(owner, args);
trace(this, this.name, "methodCall()", "realMethodName=[" + realMethodName + "]", "return value=" + returnValue);
return returnValue;
}
}
return null;
}
}
}
接下來,這裡要額外特別說明一點,因為我使用到自定 Metadata 的技術,所以下面這個專案的編譯參數須增加設定:
-keep-as3-metadata+=PublicMethod
/External2.as,會編譯成 External2.swf
package
{
import external.comp.MyComp2;
import flash.display.Sprite;
import flash.events.Event;
import flash.utils.describeType;
public class External2 extends Sprite
{
public function External2()
{
trace(this, this.name);
new MyComp2;
this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void{
this.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
trace(this, this.name, "onAddedToStage()");
}
[PublicMethod(name="job1")]
public function doJob1():void{
trace(this, this.name, "doJob1()");
}
[PublicMethod(name="job2")]
public function doJob2(name:String):String{
var resp:String = "Hello, " + name;
trace(this, this.name, "doJob2()", "resp=" + resp);
return resp;
}
[PublicMethod(name="getMyCompClassName")]
public function getMyCompClassName():String{
var resp:String = String(describeType(MyComp2)["@name"]).replace("::", ".");
trace(this, this.name, "getMyCompClassName()", "resp=" + resp);
return resp;
}
}
}
/external/comp/MyComp2.as,會被 External2.swf 所包含
package external.comp
{
import flash.display.Sprite;
import flash.events.Event;
public class MyComp2 extends Sprite
{
public function MyComp2()
{
trace(this, this.name);
this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void{
this.removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
trace(this, this.name, "onAddedToStage()");
}
[PublicMethod(name="job1")]
public function doJob1():void{
trace(this, this.name, "doJob1()");
}
[PublicMethod(name="job2")]
public function doJob2(name:String):String{
var resp:String = "Hello, " + name;
trace(this, this.name, "doJob2()", "resp=" + resp);
return resp;
}
}
}
執行結果如下:
[object Main2] root1 [object External2] instance2 [object MyComp2] instance3 [object External2] instance2 onAddedToStage() [object Main2] root1 methodCall() owner=[object External2] methodName=job1 args=null [object External2] instance2 doJob1() [object Main2] root1 methodCall() realMethodName=[doJob1] return value=undefined [object Main2] root1 methodCall() owner=[object External2] methodName=job2 args=Ben [object External2] instance2 doJob2() resp=Hello, Ben [object Main2] root1 methodCall() realMethodName=[doJob2] return value=Hello, Ben [object Main2] root1 onLoaderCompleteHandler() doJob2() return value=Hello, Ben [object Main2] root1 methodCall() owner=[object External2] methodName=getMyCompClassName args=null [object External2] instance2 getMyCompClassName() resp=external.comp.MyComp2 [object Main2] root1 methodCall() realMethodName=[getMyCompClassName] return value=external.comp.MyComp2 [object Main2] root1 onLoaderCompleteHandler() getMyCompClassName() return value=external.comp.MyComp2 [object MyComp2] instance4 [object MyComp2] instance4 onAddedToStage() [object Main2] root1 methodCall() owner=[object MyComp2] methodName=job1 args=null [object MyComp2] instance4 doJob1() [object Main2] root1 methodCall() realMethodName=[doJob1] return value=undefined [object Main2] root1 methodCall() owner=[object MyComp2] methodName=job2 args=Ben [object MyComp2] instance4 doJob2() resp=Hello, Ben [object Main2] root1 methodCall() realMethodName=[doJob2] return value=Hello, Ben [object Main2] root1 onLoaderCompleteHandler() instance.doJob2() return value=Hello, Ben
混淆:
cd C:\works\MainExternalInteractiveDemo "C:\Program Files\secureSWF_v3pro_win_build5762\ssCLI.exe" ./bin-debug/Main2.swf ./bin-debug -p:most_aggressive -v:10 "C:\Program Files\secureSWF_v3pro_win_build5762\ssCLI.exe" ./bin-debug/External2.swf ./bin-debug -p:most_aggressive -v:10 pause
再次執行結果:
[object Main2] root1 [object External2] instance2 [object case] instance3 [object External2] instance2 onAddedToStage() [object Main2] root1 methodCall() owner=[object External2] methodName=job1 args=null [object External2] instance2 doJob1() [object Main2] root1 methodCall() realMethodName=[do] return value=undefined [object Main2] root1 methodCall() owner=[object External2] methodName=job2 args=Ben [object External2] instance2 doJob2() resp=Hello, Ben [object Main2] root1 methodCall() realMethodName=[ else] return value=Hello, Ben [object Main2] root1 onLoaderCompleteHandler() doJob2() return value=Hello, Ben [object Main2] root1 methodCall() owner=[object External2] methodName=getMyCompClassName args=null [object External2] instance2 getMyCompClassName() resp=break. case [object Main2] root1 methodCall() realMethodName=[getMyCompClassName] return value=break. case [object Main2] root1 onLoaderCompleteHandler() getMyCompClassName() return value=break. case [object case] instance4 [object case] instance4 onAddedToStage() [object Main2] root1 methodCall() owner=[object case] methodName=job1 args=null [object case] instance4 doJob1() [object Main2] root1 methodCall() realMethodName=[do] return value=undefined [object Main2] root1 methodCall() owner=[object case] methodName=job2 args=Ben [object case] instance4 doJob2() resp=Hello, Ben [object Main2] root1 methodCall() realMethodName=[ else] return value=Hello, Ben [object Main2] root1 onLoaderCompleteHandler() instance.doJob2() return value=Hello, Ben
值得觀察的地方是,在 methodCall() 中的 log 訊息,realMethodName 表示 External2.swf 或其 MyComp2 所提供的 method 已經被混淆更名後,我們仍然能透過 metadata 找到對應的 method 參考,然後正確的執行。
更值得注意的是,混淆後,MyComp2 的完整類別名稱也已經不是 external.comp.MyComp2 了,而成為 "break. case" 這種看不懂的位置,所以我們也應該要以這個新的字串去映射到對應的類別。
0 意見:
張貼意見