Adobe Air & Adobe Flex & ActionScript & Mobile Dev & HTML5 & RIA & User Experience
最近開始使用 RobotLegs,覺得還蠻好用的,可以少寫很多程式碼
雖然有一些效能上的問題,但可以透過自己管理創建/銷毀時機方式避開
當想要把它用在 Flex Module 開發時,又遇到了一些問題
找不到任何良好的 RobotLegs Modular 開發範例
雖然有人寫了 Modular Utilities,且有提供範例 ModularDoodads
但是坦白說,寫的很糟糕
Module 與主程式綁在一起,無法拆成獨立外部檔案
又有人基於 Modular Utilities 寫了另一個範例 DynModules
終於將 Module 拆成外部檔案了
不過仔細一看,原來它 Hard Code 硬將 Module 載入到 ApplicationDomain.currentDomain
表示 Module 永遠無法卸載,又是另一個骯髒的範例
於是只好自己想辦法解決這問題
我的初步需求很簡單,Module 並不需要與主程式共用 Context or Injector
Module 有自己獨立的 Context,只是與主程式共用 RobotLegs Lib. 而已
僅僅只是這樣都遇到了問題
以下便是簡化的測試程式:
Main.mxml
<?xml version="1.0" encoding="utf-8"?> <s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx"> <fx:Script> <![CDATA[ import org.robotlegs.mvcs.*; // include RobotLegs framework. Actor; Command; Context; Mediator; ]]> </fx:Script> <s:ModuleLoader url="modules/RLModule.swf" /> </s:Application>
modules/RLModule.mxml
<?xml version="1.0" encoding="utf-8"?> <s:Module xmlns:fx="http://ns.adobe.com/mxml/2009" xmlns:s="library://ns.adobe.com/flex/spark" xmlns:mx="library://ns.adobe.com/flex/mx" xmlns:modules="modules.*"> <fx:Declarations> <modules:RLModuleContext contextView="{this}" /> </fx:Declarations> </s:Module>
modules/RLModuleContext.as
package modules { import flash.system.ApplicationDomain; import org.robotlegs.mvcs.Context; public class RLModuleContext extends Context { override public function startup():void{ mediatorMap.mapView(RLModule, RLModuleMediator); } } }
modules/RLModuleMediator.as
package modules { import org.robotlegs.mvcs.Mediator; public class RLModuleMediator extends Mediator { [Inject] public var view:RLModule; } }
不管有沒有對 RLModule 最佳化,執行就會得到錯誤訊息
ReferenceError: Error #1065: 變數 RLModule 未定義。 at flash.system::ApplicationDomain/getDefinition() at org.swiftsuspenders.injectionpoints::PropertyInjectionPoint/applyInjection() at org.swiftsuspenders::Injector/injectInto() at org.swiftsuspenders::Injector/instantiate() at org.robotlegs.base::MediatorMap/createMediatorUsing() at org.robotlegs.base::MediatorMap/mapView() at modules::RLModuleContext/startup() at org.robotlegs.mvcs::Context/onAddedToStage() at flash.display::DisplayObjectContainer/addChildAt() ...
花了一些時間在 RobotLegs 原始碼中找原因
才發現 Context 會替 mediatorMap, commandMap, viewMap 建立 childInjector
而 childInjector 又需要用到 contextView 的 ApplicationDomain
可是一開始 Module 還沒加到舞台上時,loaderInfo 是 null
備份方案變成改取 ApplicationDomain.currentDomain
也就是 Context 類別定義的那一個
當然裡面根本不會有 Module 的類別
結果就是一堆 Error
解決方式就是在 RLModuleContext 自行覆寫一次 getApplicationDomainFromContextView
內容直接從 super 抄一遍就好了
override protected function getApplicationDomainFromContextView():ApplicationDomain{
if (contextView && contextView.loaderInfo)
return contextView.loaderInfo.applicationDomain;
return ApplicationDomain.currentDomain;
}
Adobe Flash Player11 推出後,最令人開心的是支援 GPU 運算
可以透過顯卡來幫忙,不在只有以往的 CPU。
但要自已從無到有的從底層寫,好像有點辛苦
奶小茶我今天就是要介紹的是一套 2D flash API, 一樣底層是支援 GPU 運算的
StarlingFramework, The GPU powered 2D Flash API
http://www.starling-framework.org/
可以先到官網下載 Source 和 Sample Code.
編譯時,一定要選擇 Flash Player11 才行
這套 FrameWork 的好處,就是寫法長的跟本來的差不多
一樣是叫 Sprite, TextField, Stage,
一樣可以有 addChild 等方法,學起來不會太難
看一下試寫的效果,結合 Box2D, 300個Box, 還可以保持在 30FPS 左右

使用 SecureSWF 做包含外部模組的專案時,遇到混淆過頭
導致外部 SWF 模組無法正常執行的問題
當然基本的共用類別需要必須先排除在外
可是還是無法正常執行
測試簡化問題之後,發生條件如下
主程式 SWF 包含一共享父類別定義
該類別宣告了 protected getter/setter member
且型別是一個 interface
外部子 SWF 模組繼承此共享父類別
當兩個 SWF 混淆過之後 (排除共享父類別)
外部子 SWF 模組載入後,嘗試存取 super protected member
會得到 ReferenceError: Error #1065: 變數 default::foo 未定義。
以下是測試原始碼:
Main.as
package { import com.ticore.obfu.SupClass; import flash.display.Loader; import flash.display.Sprite; import flash.events.Event; import flash.net.URLRequest; import flash.system.ApplicationDomain; import flash.system.LoaderContext; /** * Main 主程式 SWF * 持有共用的 SupClass 類別定義 * 依據自身 application domain 產生 child domain * 用來載入繼承 SupClass 的外部 SWF 模組 */ [SWF(width="300", height="200")] public class Main extends Sprite { public var currAppDom:ApplicationDomain = ApplicationDomain.currentDomain; public var childAppDom:ApplicationDomain = new ApplicationDomain(currAppDom); public var ldr:Loader = new Loader(); public var req:URLRequest = new URLRequest("Mod.swf"); public var ldrCxt:LoaderContext = new LoaderContext(false, childAppDom); public function Main() { // 明確引用父類別定義,編入 parent application domain SupClass; ldr.contentLoaderInfo.addEventListener(Event.COMPLETE, onLoadCompleteHandler); ldr.load(req, ldrCxt); addChild(ldr); } public function onLoadCompleteHandler(e:Event):void{ trace("onLoadCompleteHandler();"); } } }
com/ticore/obfu/SupClass.as
package com.ticore.obfu { import flash.display.Bitmap; import flash.display.IBitmapDrawable; import flash.display.Sprite; /** * 共用的 SupClass 類別定義 * 帶有 protected getter/setter 且型別為 interface */ public class SupClass extends Sprite { protected var _foo_:IBitmapDrawable = new Bitmap(); protected function get foo():IBitmapDrawable { return _foo_; } protected function set foo(value:IBitmapDrawable):void{ _foo_ = value; } } }
Mod.as
package { import com.ticore.obfu.SupClass; import flash.display.Sprite; import flash.text.TextField; /** * 外部 SWF 模組 * 繼承共用的 SupClass 類別定義 * 嘗試用不同方式存取 SupClass protected getter member */ [SWF(width="300", height="200")] public class Mod extends SupClass { public var txt:TextField = new TextField(); public function Mod() { init(); doTrace(new Date()); // 直接呼叫 super protected getter member 會得到 error try { doTrace("foo:", foo); } catch (e:Error) { doTrace(e); } // 用 with block with (0) { doTrace("foo:", foo); } // 用關聯陣列存取算子 doTrace("foo:", this["foo"]); } public function init():void{ txt.wordWrap = true; txt.width = 250; txt.height = 150; txt.x = txt.y = 25; txt.border = true; addChild(txt); } public function doTrace(...args):void{ trace.apply(null, args); txt.appendText(args.join(" ") + "\n"); } } }
用來混淆的 Ant Task
<?xml version="1.0" encoding="utf-8"?>
<project name="SecureSWF Test" default="protect main" basedir=".">
<property environment="env" />
<!-- secureSWF Pro v3.61.5987 -->
<taskdef name="protect"
classpath="${env.SECURE_SWF_HOME}\secureSWF.jar"
classname="secureSWF.ant.ProtectTask" />
<target name="protect main">
<protect outputPath="secure">
<fileset dir="bin-release" includes="**.swf"/>
<rule filter="com.ticore.obfu.*" rename="false" />
</protect>
</target>
</project>
非常神奇的是,用 ASV 觀察混淆過的 SWF
明明沒有任何混淆的跡象,可是卻會造成執行失敗
而用 with block 與關聯陣列運算子,卻又能避開問題
解決方式除了上面兩種方式
SecureSWF 還有定義一個參數是 renameProtectedNamespaces
設為 false or off 就可以了
很高兴Richmedia+又增加了一名实力非常强劲的成员。
他就是来自台湾的milkmidi。
milkmidi
一直以来,关于RIA开发的精品文章很多,但是几年过后,能够坚持下来又频繁更新的成员很少,Richmedia+希望能把一大批优秀的人才聚集在一起,一起来更新关于RIA的文章,甚至是任何跟互联网有关的文章。Richmedia+致力于成为RIA领域专家的博客聚合地。
Richmedia+目前成员(排名按字母):
Richmeida+是开放的,非营利性的组织,成为成员之后你将会拥有Richmedia+ Blog的所有权限。再次强调,是所有权限!
欢迎各位有兴趣的同学加入这个开放的大家庭,如果感兴趣,请联系 aedisju#gmail.com
最近 Flash Player 11 正式版發表不久
網友 Vincent 留言發現空 Array 的 splice 操作會出現問題
測試程式如下
package { import flash.display.Sprite; import flash.system.Capabilities; import flash.text.TextField; [SWF(width="300", height="200")] public class FL11ArrayIssue extends Sprite { public function FL11ArrayIssue() { trace(Capabilities.version, Capabilities.playerType); var ary:Array = new Array(5); /*/ 各種修正 FL 11 Array 的方式 ary.push(undefined); ary.pop(); ary.length++; ary.length--; ary.shift(); ary.unshift(undefined); ary[0] = undefined; ary[0] = null; ary[0] = ary[0]; //*/ trace("[" + ary.join(" , ") + "]"); ary.splice(2, 1, "X"); trace("[" + ary.join(" , ") + "]"); ary.splice(2, 1, "Y"); trace("[" + ary.join(" , ") + "]"); ary.splice(2, 1, "Z"); trace("[" + ary.join(" , ") + "]"); } } }
輸出結果
WIN 11,0,1,152 StandAlone
[ , , , , ]
[ , , X , , ]
[ , Y , , , ]
[ , Y , Z , , ]
解決方式都寫在註解裡面了,最簡單的方式就是明確指定元素 1 位置的數值
需要注意是,一旦 Array.length 縮減成 0,又重新拉長
就需要再次修正一下 Array