RobotLegs Module 開發上的問題

22 2011 In: ActionScript3, Flash, Flex

最近開始使用 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;
  }

原始檔案下載

分享到新浪微博 分享到人人网 分享到豆瓣 分享到鲜果 分享到百度空间 分享到开心网 QQ书签 分享到YAHOO! 分享到Google Google Buzz 分享到Facebook 分享到Plurk Digg delicious Technorati Twitter

StarlingFramework

22 2011 In: ActionScript3, Flash

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 左右

分享到新浪微博 分享到人人网 分享到豆瓣 分享到鲜果 分享到百度空间 分享到开心网 QQ书签 分享到YAHOO! 分享到Google Google Buzz 分享到Facebook 分享到Plurk Digg delicious Technorati Twitter

SecureSWF 與外部模組的陷阱

21 2011 In: ActionScript3, Flash

使用 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 就可以了

測試用的 Flash Builder Project 原始檔案下載

分享到新浪微博 分享到人人网 分享到豆瓣 分享到鲜果 分享到百度空间 分享到开心网 QQ书签 分享到YAHOO! 分享到Google Google Buzz 分享到Facebook 分享到Plurk Digg delicious Technorati Twitter

milkmidi 加入 Richmedia+

21 2011 In: Life is Cool!

很高兴Richmedia+又增加了一名实力非常强劲的成员。
他就是来自台湾的milkmidi

milkmidi

一直以来,关于RIA开发的精品文章很多,但是几年过后,能够坚持下来又频繁更新的成员很少,Richmedia+希望能把一大批优秀的人才聚集在一起,一起来更新关于RIA的文章,甚至是任何跟互联网有关的文章。Richmedia+致力于成为RIA领域专家的博客聚合地。

Richmedia+目前成员(排名按字母):

Richmeida+是开放的,非营利性的组织,成为成员之后你将会拥有Richmedia+ Blog的所有权限。再次强调,是所有权限!

欢迎各位有兴趣的同学加入这个开放的大家庭,如果感兴趣,请联系 aedisju#gmail.com

分享到新浪微博 分享到人人网 分享到豆瓣 分享到鲜果 分享到百度空间 分享到开心网 QQ书签 分享到YAHOO! 分享到Google Google Buzz 分享到Facebook 分享到Plurk Digg delicious Technorati Twitter

Flash Player 11 Array Bug

12 2011 In: ActionScript3, Flash

最近 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

分享到新浪微博 分享到人人网 分享到豆瓣 分享到鲜果 分享到百度空间 分享到开心网 QQ书签 分享到YAHOO! 分享到Google Google Buzz 分享到Facebook 分享到Plurk Digg delicious Technorati Twitter