其實,就算不做混淆,main.swf 載入 external.swf 時,若使用了全新的 ApplicationDomain 的話,Singleton 的應用就已經會出問題了~
/Main.as
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import idv.ben.util.MySingleton;
[SWF(width="400", height="300")]
public class Main extends Sprite
{
public function Main()
{
trace(this, "MySingleton's name is " + MySingleton.getInstance().getName());
var ldr:Loader = new Loader();
ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, on_ldr_complete);
ldr.load(new URLRequest("External.swf"));
var ldr2:Loader = new Loader();
ldr2.contentLoaderInfo.addEventListener(Event.COMPLETE, on_ldr2_complete);
var ctx2:LoaderContext = new LoaderContext();
ctx2.applicationDomain = new ApplicationDomain();
ldr2.load(new URLRequest("External.swf"), ctx2);
trace("");
}
private function on_ldr_complete(e:Event):void{
trace(this, "on_ldr_complete()");
var info:LoaderInfo = e.currentTarget as LoaderInfo;
info.removeEventListener(Event.COMPLETE, on_ldr_complete);
var ldr:Loader = info.loader;
this.addChild(ldr);
trace("");
}
private function on_ldr2_complete(e:Event):void{
trace(this, "on_ldr2_complete()");
var info:LoaderInfo = e.currentTarget as LoaderInfo;
info.removeEventListener(Event.COMPLETE, on_ldr2_complete);
var ldr:Loader = info.loader;
this.addChild(ldr);
trace("");
}
}
}
/External.as
package
{
import flash.display.Sprite;
import flash.events.Event;
import idv.ben.util.MySingleton;
[SWF(width="400", height="300")]
public class External extends Sprite
{
public function External()
{
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
trace(this, "MySingleton's name is " + MySingleton.getInstance().getName());
}
}
}
以上兩個輸出的 swf,都會用到以下類別,
/idv/ben/util/MySingleton.as
package idv.ben.util
{
import flash.utils.getTimer;
public class MySingleton
{
static private var _name:String = (Math.random() * 10000).toFixed();
static private var _instance:MySingleton;
static public function getInstance():MySingleton{
if(!_instance)
_instance = new MySingleton(new InnerClass);
return _instance;
}
function MySingleton(ic:InnerClass){}
public function getName():String{
trace("MySingleton[" + _name + "].getName()");
return _name;
}
}
}
internal class InnerClass{}
輸出結果:
MySingleton[8288].getName() [object Main] MySingleton's name is 8288 [object Main] on_ldr2_complete() MySingleton[5357].getName() [object External] MySingleton's name is 5357 [object Main] on_ldr_complete() MySingleton[8288].getName() [object External] MySingleton's name is 8288
可看出 ldr2 使用 new ApplicationDomain 後,取得 singleton 物件就已經不是同一個物件了。
若再使用以下 command line 作混淆的話:
set SECURE_SWF_HOME=E:\Program Files (x86)\secureSWF_v3pro_win_build5762 set PROJECT=E:\works_test\TestMainExternalSingleton java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/Main.swf %PROJECT%/bin-debug -p:most_aggressive -v:10 java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/External.swf %PROJECT%/bin-debug -p:most_aggressive -v:10 pause
重新執行,輸出結果如下,就連 ldr 載入的 swf 所取得的 singleton 物件也不會是同一個,因為在 main.swf 與 external.swf 中所各自存放的 class name 已經各自不同,所以已經被視同為兩個不同的類別,有著各自的 singleton 物件了:
MySingleton[868].getName() [object Main] MySingleton's name is 868 [object Main] on_ldr2_complete() MySingleton[8773].getName() [object External] MySingleton's name is 8773 [object Main] on_ldr_complete() MySingleton[8655].getName() [object External] MySingleton's name is 8655
接下來,調整程式,使用 metadata 來找 method name 的作法,
/Main2.as
package
{
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.net.URLRequest;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
import flash.utils.describeType;
import idv.ben.util.MySingleton2;
[SWF(width="400", height="300")]
public class Main2 extends Sprite
{
public function Main2()
{
trace(this, "MySingleton2", MySingleton2.getInstance().getName());
trace(this, "MySingleton2", MySingleton2.getInstance().getPropValue("abc"));
var ldr3:Loader = new Loader();
ldr3.contentLoaderInfo.addEventListener(Event.COMPLETE, on_ldr3_complete);
var ctx3:LoaderContext = new LoaderContext();
ctx3.applicationDomain = new ApplicationDomain();
ldr3.load(new URLRequest("External2.swf"), ctx3);
trace("");
}
private function on_ldr3_complete(e:Event):void{
trace(this, "on_ldr3_complete()");
var info:LoaderInfo = e.currentTarget as LoaderInfo;
info.removeEventListener(Event.COMPLETE, on_ldr3_complete);
var ldr:Loader = info.loader;
methodCall(ldr.content, "setSingletonDelegate", [MySingleton2.getInstance()]);
this.addChild(ldr);
trace("");
}
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;
}
}
}
/External2.as
package
{
import flash.display.Sprite;
import flash.events.Event;
import idv.ben.util.MySingleton2;
[SWF(width="400", height="300")]
public class External2 extends Sprite
{
public function External2()
{
addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
}
private function onAddedToStage(e:Event):void{
removeEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
trace(this, "MySingleton2's name is ", MySingleton2.getInstance().getName());
trace(this, "MySingleton2's prop is ", MySingleton2.getInstance().getPropValue("xyz"));
}
[PublicMethod(name="setSingletonDelegate")]
public function setSingletonDelegate(delegate:Object):void{
trace(this, "setSingletonDelegate()", delegate);
MySingleton2.setDelegateInstance(delegate);
}
}
}
上面兩段程式碼,當 External2.swf 被 Main2.swf 載入完成後,會由 Main2.swf 將 MySingleton2 的 instance 塞進 External2.swf 中的 MySingleton2 中。
下面開始,MySingleton2 類別的架構有些不同之處,在於多了一個 setDelegateInstance() 方法,可以讓 MySingleton2 實際上不是使用自己建立出來的物件,而是使用別人提供的物件:
/idv/ben/util/MySingleton2.as
package idv.ben.util
{
import flash.utils.getTimer;
public class MySingleton2
{
static private var _name:String = (Math.random() * 10000).toFixed();
static private var _prop:String = (Math.random() * 10000).toFixed();
static private var _instance:MySingleton2;
static public function getInstance():MySingleton2{
if(!_instance)
_instance = new MySingleton2();
return _instance;
}
static public function setDelegateInstance(delegate:Object):void{
_instance = new MySinbleton2Wrapper(delegate);
}
[PublicMethod(name="getName")]
public function getName():String{
trace("MySingleton2[" + _name + "].getName()");
return _name;
}
[PublicMethod(name="getPropValue")]
public function getPropValue(propName:String):String{
trace("MySingleton2[" + _name + "].getPropValue(" + propName + ")");
return _name + "'s [" + propName + "] is " + _prop;
}
}
}
/ben/idv/util/MySingleton2Wrapper.as
package idv.ben.util
{
import flash.utils.describeType;
public class MySinbleton2Wrapper extends MySingleton2
{
private var _delegate:Object;
public function MySinbleton2Wrapper(delegate:Object){
_delegate = delegate;
}
private function methodCall(methodName:String, args:Array=null):*{
var xml:XML = describeType(_delegate);
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:* = _delegate[realMethodName].apply(_delegate, args);
return returnValue;
}
}
return null;
}
override public function getName():String{
return methodCall("getName");
}
override public function getPropValue(prop1:String):String{
return methodCall("getPropValue", [prop1]);
}
}
}
輸出結果:
MySingleton2[7110].getName() [object Main2] MySingleton2 7110 MySingleton2[7110].getPropValue(abc) [object Main2] MySingleton2 7110's [abc] is 6545 [object Main2] on_ldr3_complete() [object Main2] root1 methodCall() owner=[object External2] methodName=setSingletonDelegate args=[object MySingleton2] [object External2] setSingletonDelegate() [object MySingleton2] [object Main2] root1 methodCall() realMethodName=[setSingletonDelegate] return value=undefined MySingleton2[7110].getName() [object External2] MySingleton2's name is 7110 MySingleton2[7110].getPropValue(xyz) [object External2] MySingleton2's prop is 7110's [xyz] is 6545
可以看出,即使使用 new ApplicationDomain,也可以使用到同一份由 Main2.swf 所提供的 singleton 物件。
經以下 command line 作混淆:
set SECURE_SWF_HOME=E:\Program Files (x86)\secureSWF_v3pro_win_build5762 set PROJECT=E:\works_test\TestMainExternalSingleton java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/Main2.swf %PROJECT%/bin-debug -p:most_aggressive -v:10 java -jar "%SECURE_SWF_HOME%\secureSWF.jar" %PROJECT%/bin-debug/External2.swf %PROJECT%/bin-debug -p:most_aggressive -v:10 pause
再次執行,輸出結果:
MySingleton2[5757].getName() [object Main2] MySingleton2 5757 MySingleton2[5757].getPropValue(abc) [object Main2] MySingleton2 5757's [abc] is 9602 [object Main2] on_ldr3_complete() [object Main2] root1 methodCall() owner=[object External2] methodName=setSingletonDelegate args=[object MySingleton2] [object External2] setSingletonDelegate() [object MySingleton2] [object Main2] root1 methodCall() realMethodName=[setSingletonDelegate] return value=undefined MySingleton2[5757].getName() [object External2] MySingleton2's name is 5757 MySingleton2[5757].getPropValue(xyz) [object External2] MySingleton2's prop is 5757's [xyz] is 9602
仍可抓到同一個 singleton 物件。
0 意見:
張貼意見