[转]使用RSL来减小Flex生成的SWF文件的大小
Flex应用体积过大,下载到客户端效率低下,有什么解决方案?
将业务功能划分模块,创建Module分别实现每个模块,然后在应用中使用时动态加载;
RSL方式编译。
减小应用SWF文件大小的一个方法就是将一些共享的外部资源拆分出去, 成为一个独立的文件, 这样可以单独地加载缓存到客户端. 这些共享资源可以由多个应用在运行时进行加载, 但是传递到客户端的动作只会发生一次. 这些共享文件被称为运行时共享库(Runtime Shared Libraries)或者简写为RSL.
如果你有多个应用而且这些应用共享一些核心组件或者类, 那么作为RSL,用户只会唯一的一次加载这些资源. 只要应用在同一个域中, 这些应用共享同一个缓存的RSL, 这样应用文件的大小就减小了. 使用RSL的应用越多, 效果越好, 如果只有一个应用, 总的文件大小不但不减小,反而会增大.
你既可以使用Flex Builder通过项目选项来创建Flex的Library项目,也可以使用compc这样的命令行. 编译好RSL以后, 可以在编译应用时把Library的位置传递给编译器.
下面是适合使用RSL的典型用例:
* 大型应用, 需要使用通用组件库加载多个小型应用. 最顶级的应用和下级应用可以共享存储在RSL中的组件.
* 在一个服务器上的使用通用组件库的系列应用. 当用户操作第一个应用时, 用户下载应用的SWF文件和RSL. 当操作第二个应用时, 用户就只需要下载应用本身的SWF文件就行了.
* 一个独立的应用使用RSL的意义在于: 如果这个应用本身会频繁的修改, 而有一部分组件是极少改动的. 在这种情况下, 使用RSL的好处是: 组件下载一次, 而应用可以多次下载.
2———————-
理解链接可以帮助你理解RSL是怎样工作的. Flex编译器支持静态链接和动态链接. 静态链接是最通常的方法. 但是动态链接使你借助于RSL来实现SWF文件的缩水以及应用的下载次数.
当你使用静态链接时, 编译器将应用中所有引用的类和依赖生成到最终的SWF文件中, 这个文件会比较大, 下载也会比较慢, 但是下载完毕后, 运行会比较快, 因为SWF文件中已经包含了所有的代码.
如果你的应用中使用了类库, 那么你需要使用类路径或者是添加SWC文件. 如果是使用类路径, 编译器会将类路径中用到的那部分类打包生成到SWF文件中. 如果是使用SWC文件, 编译器会将整个SWC文件打包到SWF文件中.
动态链接是这样的: 一个应用要使用的一部分类存在于一个外部的文件中, 运行时动态加载. 这样的话, 主SWF文件可以小一些, 但是应用依赖于运行时加载的外部文件. RSL就是使用动态链接的.
如果想使用动态链接类, 需要把它们编译成库. 然后编译器将库中的内容从SWF文件剔除出去. 而且必须在编译时提供链接检查.
为指定哪些文件是动态链接的,需要使用外部库路径选项,外部选项或者外部加载编译选项. 这些选项告诉编译器从应用中去除此部分内容, 而预备在运行时调用. 外部选项为动态链接指定了单独的类. 外部加载选项指定了一个XML文件, 描述了动态链接的类.
指定SL的外部资源的顺序是非常关键的, 因为被其他类调用的基础类必须被首先加载.
你还要用runtime-shared-libraries选项来指定RSL的位置.
你可以使用link-report这个编译选项来查看应用的链接信息.
3———————-
RSL也需要谨慎使用
RSL也不是对于所有的应用都是有益的. 需要对应用RSL前后的下载时间和启动时间都测试过, 才能得到正确的结论.
RSL不能跨域共享. 如果客户在一个域中使用了RSL, 然后运行了另一个域的应用, 虽然这两个RSL是相同的, 但是需要下载两次.
RSL 通常会增加应用的启动时间. 这是应用不管整个库实际如何使用, 只是简单地全部加载整个库. 就这一点来说, RSL越小越好. 这与静态链接库的使用是不同的. 当你编译一个Felx应用时, 编译器只解开需要的组件. 一般来说, 库的大小可以是任意的, 它只影响编译时间而不会影响下载的时间.
如果在好几个应用中使用相同的组件库, 那么可以考虑合并这些库, 形成一个RSL. 但是如果库合并后, 每个应用只会用到其中的一小部分, 那么还不如多加载几个小RSL更高效.
如果一些类重复打包在多个RSL中, 那么一定要注意同步更新的问题.
RSL不能应用在基类是Sprite或者MovieClip的纯ActionScript项目中. 因为RSL需要基类知道如何加载RSL, 比如: Application或者SimpleApplication.
关于 framework.swc文件
framework.swc 是一个标准的SWC文件. 缺省地它不能用作RSL. 整个framwork.swc文件不被链接到任何一个应用中. Flex编译器只将那些应用用到的部分链接到生成最后的SWF文件. 比如: 如果一个应用只使用了Button, Panel和TextArea控件, 那么只有这几个控件和它们的依赖项被编译器链接.
几乎所有的应用都需要framework.swc文件的一部分, 但是它并不适合作为RSL. 原因如上据说, RSL是整个链接, 不管实际使用多少的. 如果RSL包含了很多类, 而应用只使用了其中的一小部分, 那么这样的加载方式并不是最合理的. 这样使用会造成应用的启动时间大大增加.
RSL的优点
下面的一个例子说明了将几个的共享组件做成RSL的优点. 在这个例子中, 组件库的大小是150K, 编译后的应用的大小是100K.
使用了RSL, RSL只被下载一次. 那么合计下载量是350K, 节约了30%. 如果再添加第3个, 第4个应用的话, 每次都能150K的下载量.
一般来说, 在一个域中使用同一个RSL的应用越多, 那么好处就越大.
4———————-创建库
可以使用Flex Builder或者Compc命令行来创建库. 库可以是一个SWC文件, 或者是包含了library.swf和catalog.xml文件的目录. 一个库通常包含自定义组件和类. 然后就可以在RSL中使用这些库了.
在Flex Bulder中, 通过使用Flex Library Build Path对话框来添加资源到库中.
在命令行中, 使用include-classes和include-namespaces选项来添加文件到库中.
下面的命令行示例说明了如何创建一个名字叫CustomCellRenderer的库:
compc -source-path ../mycomponents/components/local
-include-classes CustomCellRendererComponent -directory=true -debug=false
-output ../libraries/CustomCellRenderer
所有包含的组件必须是静态链接的文件. 使用compc编译器创建库时, 不能使用include-file选项, 因为这个选项不是将library.swf文件静态链接到库中的.
可以使用directory选项指定输出到一个目录而不是到一个SWC文件中:
mycomponents/components/local
libraries/CustomCellRenderer
true
false
CustomCellRendererComponent
输出会是一个目录,目录里包含两个文件
* catalog.xml
* library.swf
创建library.swf文件后, 你可以编译应用并且指定文件的位置.
5———————-
在编译应用时要使用RSL, 需要使用下列编译选项:
* runtime-shared-libraries 提供运行运行时共享库的位置.
* external-library-path|externs|load-externs 提供编译时库的位置. 编译器需要这个信息动态链接.
使 用runtime-shared-libraries选项来指定SWF文件的位置, 这样应用能够在运行时加载RSL. 需要指定SWF与部署位置的相对路径. 比如: 如果把library.swf文件放在web_root/libraries目录下, 而应用在web_root目录下, 那么文件的指定方法是: libraries/library.swf
可以用这个选项指定多个库. 如果指定了多个库, 需要用逗号分隔.
使 用external-library-path选项来指定library在编译时的SWC文件或者目录的位置. 编译器会在编译时根据这个选项进行链接的检查. 你还可以使用externs或者load-externs选项来指定其他单独的classes或者xml文件来定义库的内容.
下面是一个编译MyApp应用的命令行示例, 其中使用了2个库:
mxmlc -runtime-shared-libraries=
../libraries/CustomCellRenderer/library.swf,
../libraries/CustomDataGrid/library.swf
-external-library-path=../libraries/CustomCellRenderer,
../libraries/CustomDataGrid MyApp.mxml
库的顺序非常重要, 因为基础类必须先加载.
你先可以使用配置文件, 示例如下:
../libraries/CustomCellRenderer
../libraries/CustomDataGrid
../libs/playerglobal.swc
../libraries/CustomCellRenderer/library.swf
../libraries/CustomDataGrid/library.swf
runtime-shared-libraries选项值是library.swf文件是相对部署目录的路径. external-library-path选项是编译时SWC文件的路径. 因此, 必须先知道库的部署路径.
示例中, 编译时文件结构如下:
c:/appfiles/MyApp.mxml
c:/libraries/CustomCellRenderer/CustomCellRenderer.swc
c:/libraries/CustomDataGrid/CustomDataGrid.swc
library.swf在编译进不是必需的. Flex编译器不验证SWF文件的存在与否, 但会把路径信息编译进行最后的应用代码中.
文件的部署结构如下:
web_root/MyApp.swf
web_root/libraries/CustomCellRenderer/library.swf
web_root/libraries/CustomDataGrid/library.swf
6———————-
这个例子包括了应用中使用RSL的完整流程。使用命令行进行编译,但是你可以使用FlexBuilder用相同的过程来创建使用RSL。
记住SWC文件是一个包含SWF文件的二进制文件,而SWF文件包含运行时的定义和附属元数据。你可以用压缩工具比如WinZip来打开SWC文件。
在使用RSL之前,首先需要了解如何静态链接一个SWC文件。
在这个例子中,应用有一个app.mxml文件,使用ProductConfigurator.as和ProductView.as。文件目录如下:
project/src/app.mxml
project/libsrc/ProductConfigurator.as
project/libsrc/ProductView.as
project/lib/
project/bin/
编译这个应用时,可以使用source-path选项将/libsrc目录下的类链接进来,方法如下:
cd project/src
mxmlc -o=../bin/app.swf -source-path+=../libsrc app.mxml
这个命令添加ProductConfigurator和ProductView类到SWF文件中。
如果要创建库,可以用compc来创建SWC文件,用下面的命令:
cd project
compc -source-path+=libsrc -debug=false -o=lib/mylib.swc
ProductConfigurator ProductView
注意要将debug选项设置为false. 生成结果是project/lib/mylib.swc文件,包含ProductConfigurator和ProductView两个类。
现在可以使用新创建的库来重新编译应用,用library-path选项来指定库,方法如下:
cd project/src
mxmlc -o=../bin/app.swf -library-path+=../lib/mylib.swc app.mxml
创建库以后,你可以用RSL来重新编译生成应用。完整的步骤如下:
1. 指示编译器不要将库链接到应用中。
2. 准备RSL,以便于在运行时使用。
3. 指示编译器生成附加元数据用于加载RSL。
第一步是指定编译生成应用时库中的哪些类需要排除在外。主要是使用external-library-path选项,如下面的例子所示:
cd project/src
mxmlc -o=../bin/app.swf -external-library-path+=../lib/mylib.swc app.mxml
如 果你尝试运行app.swf,Flash Player会抛出一个运行时异常。因为ProductConfigurator和ProductView类还未定义。external-library -path配置选项告诉编译器编译这些库,但是忽略了定义。你也可以使用externs选项,但是一般来说,使用external-library- path更方便。
下一步是准备RSL以便于能在运行时找到它。首先从SWC文件中将library.swf解压出来。
下面是如何解压的例子:
cd project/lib
unzip mylib.swc library.swf
mv library.swf ../bin/myrsl.swf
此例子中将library.swf更名为myrsl.swf,并把它移动到应用SWF文件所在的目录。
最后一步是使用RSL重新编译应用。主要是使用runtime-shared-libraries选项,方法如下:
cd project/src
mxmlc -o=../bin/app.swf -external-library-path+=../lib/mylib.swc
-runtime-shared-libraries=myrsl.swf app.mxml
现在新的SWF文件会在运行应用前动态加载RSL了。