简单的说,Modular(模块)和Shell(壳)就象DLL(动态链接库)和EXE(可执行程序)之间的关系一样.例如我们在写WINDOWS的应用程序的时候可以调用大量的MICROSOFT提供的DLL里的方法(我们称之为API).同样的,我们也可以自己把功能相同或者同类的方法抽象成为动态链接库以方便我们的后续开发,升级,团队开发等等. 因此显然,Modular的推出更有利于我们开发程序的模块化.可能有人会说,这样的功能我们同样可以采用编译多个可执行SWF,然后用Loader载入不就行了?的确我不反对这样的做法,事实证明,这也可以被正确实施于未更新的版本的.不一样的是.
1.例如我们在模块中包含Application的标签,大家可以去看一下它的依赖关系就知道为什么只写一个Helloworld就会生成>100K的原因了.而事实,这些东西我们在主程序中其实是包含了这些类的.类的重复被编译导致文件变得更大.
2.Application标签是可以被运行的,简单的说,Application编译的SWF可以直接运行,这样我们的编程似乎就成了EXE和EXE的嵌套,似乎有点奇怪? 而Modular本身是不可执行的,就象DLL一样,里面可以包含很多方法也可以包含许多窗体,但事实自身是不可被运行的.而这些好处在Modular里可以轻松的实现.
好的,现在我们来直接一点吧,看一下例子.
代码下载: http://res.ezse.com/Howto/ModularDemo.rar
我们在FLEX BUILDER里建立三个项目,一个专门放MODULARS,一个为SHELL,最后一个是Interface.当然,视需要我们也可以建立多个项目.
当然,Shell(ModularMain)就是我们的主程序,相当于我们的EXE文件;
Modulars(Modulars)就是我们的DLL文件的聚集地J 当然合适的建立模块,可以使得我们的程序更具扩展性和部署.
Interface(UserModularsInterface)这不是必须的,但我建议在团队合作和大型程序开发的时候采用接口.接口的具体功能我就不在这里说了,可以找本OOD书看一下.简单的说就是模块之间混合编程所统一的必须的方法或属性.接口没有具体的实现方法,只是声明了方法或属性.
在这个例子中,我们建立了三个Modular,一个是在Shell项目中的InnerModular.mxml . 另一个是在Modulars中的OuterModular.mxml . 这个Modular和InnerModular相似. 最后一个是在Modulars中的OuterModularWithInterface.mxml . 这个Modular和前两个区别不大,仅仅是使用了Interface(推荐使用这种方式).
首先我们看一下Inner Modular. 为什么取名为Inner Modular是因为这个Modular是和Shell在同一个Project中.
InnerModular.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
[Bindable]
public var textValue:String = "(default inner text value)";
]]>
</mx:Script>
<mx:Label text="{textValue}"/>
</mx:Module>
在源程序中,唯一要注意的就是我们新建一个Application的时候,红色标记部分是引用的Application标签.把它改为Module即可.
引用中,ModularMain.mxml中
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
<mx:Script>
<![CDATA[
import mx.modules.*;
import mx.events.ModuleEvent;
private var mdInnerModular:Object;
private function init():void
{
mdInner.addEventListener(ModuleEvent.READY,onInnerModularLoaded);
}
private function onInnerModularLoaded(evt:ModuleEvent):void
{
mdInnerModular = mdInner.child;
mdInnerModular.textValue = "Inner Modular";
}
private function CallIOuterModularFunction():void
{
var iOuter:IOuterModular = mdOuterWithInterface.child as IOuterModular;
iOuter.SetText("IOuter Called");
}
]]>
</mx:Script>
<mx:ModuleLoader id="mdInner" url="InnerModular.swf"/>
<mx:ModuleLoader id="mdOuter" url="assets/OuterModular.swf" y="20"/>
<mx:Button label="Set Outer Modular Value" click="(mdOuter.child as Object).textValue = 'Outer Modular'" y="40"/>
<mx:Button label="Call Outer Modular Function" click="(mdOuter.child as Object).testFunction()" y="70"/>
<mx:ModuleLoader id="mdOuterWithInterface" url="assets/OuterModularWithInterface.swf" y="90"/>
<mx:Button label="Call Outer Modular Function" click="CallIOuterModularFunction()" y="120"/>
</mx:Application>
Shell中的有红色部分是对InnerModular的操作.注意的就是要执行Modular中的方法或者更改属性必须等ModularLoader触发了ModuleEvent.READY 后才能执行.
OuterModular.mxml:
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute">
<mx:Script>
<![CDATA[
[Bindable]
public var textValue:String = "(default outer text value)";
public function testFunction():void
{
this.textValue = "Outer Function Called";
}
]]>
</mx:Script>
<mx:Label text="{textValue}" />
</mx:Module>
内容和InnerModular一样.不一样的是放在了另一个Project中.
而我推荐的是使用Interface.下面我们就看一下使用了Interface的操作方法.
-
首先我们要定义Interface文件,为了方便,我们新建立一个Lib Project.这样编译后会生成一个swc的可被引用的代码包.
-
在Shell和Modular的Project中添加引用.
-
在Modular中实现具体的接口中定义的方法或属性.
IOuterModular.as
package
{
import flash.events.IEventDispatcher;
public interface IOuterModular extends IEventDispatcher
{
function SetText(val:String):void;
}
}
在接口中,我们定义了SetText这个方法.于是我们在Modular中先实现这方法:
OuterModularWithInterface.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Module xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" implements="IOuterModular">
<mx:Script>
<![CDATA[
[Bindable]
public var textValue:String = "(default outer(with interface) text value)";
public function SetText(val:String):void
{
this.textValue = val;
}
]]>
</mx:Script>
<mx:Label text="{textValue}" />
</mx:Module>
其中红色部分标记了此Modular为接口IOuterModular的实现. 蓝色部分则是具体实现的方法.
在ModularMain.mxml的橙色部分则是它的使用方法.
和不用接口不一样的地方是: var iOuter:IOuterModular = mdOuterWithInterface.child as IOuterModular;新建一个实体,此实体正是ModularLoader的child.这里可能要注意一下,这个child可能是Flex的Bug,这个child是一个DisplayObject,例如我们这样写mdOuterModular.child.SomeFunction().编译无法通过,提示是DisplayObject没有这个方法(废话).但是在调试模式下可以看到,这个child并非一个DisplayObject. 因此很郁闷,必须把这个child 设置成Object才能执行里面的方法.
用接口还有一个好处,那就是可以直接看到Modular中的方法和属性.而在前两个例子中是没有这种功能的.
另一个要注意的地方是,如果我们的Modular的Width,Height设置成的是100%, 而在ModularLoader中设置了Width和Height为绝对值的时候,会发现,事实上,Modular并没有填充这个固定区域! 做实验的话可以在Modular中设置一个Canvas.引用后会发现大小是0x0.所以我们可能在写这种高宽不定的时候,可能要建立一个方法,在Modular Ready后执行方法来重设置它的大小.
现在我们可以看到程序可以正常的被执行了,但是Modular生成出来的swf个个都是100多K! 当然,因为我们还没有对Modulars Project进行裁剪尺寸. 这个裁减过程的原理就是我们的主程序中已经编译了的类,在Modular中就不再编译了.而如何设置呢?在Shell的Project中的编译器的附加参数里设置为-locale en_US -link-report=d:/testreport.xml 这样在编译的时候会生成一个关于Shell里所引用过的类库列表在d:/testreport.xml的文件中. 在Modulars中的编译器的附加参数里设置为-locale en_US -load-externs=d:/testreport.xml 这样,在编译Modulars的时候,载入Shell已经编译过的类库.在编译的时候就跳过这些类库.再编译一看,大小豁然小了100K,变成了10多k.
在Dreamer的Blog上也有一篇翻译的文章大家也可以去看一下: http://www.zhuoqun.net/article.asp?id=382