View Full Version : progressive download for embedded .swf in flex3?
justme123
04-30-2009, 09:44 AM
Hi,
Imagine a small flex application (flex.swf, just a few lines of code) that embeds a big swf ([Embed (source = 'bigfile.swf')]) and uses SWFLoader to show bigfile's content after some user input (eg. authentication).
Out of the box flex.swf (that includes bigfile.swf) is downloaded completely before starting the application.
Is there a way to embed bigfile.swf and still profit from progressive download?
tia,
justme
in case you've come here and don't want to read 2 pages of try&error, check reply#17 (http://www.actionscript.org/forums/showthread.php3?p=887189)
wvxvw
04-30-2009, 11:58 AM
There is undocumented [Frame] tag, it has 2 parameters "factoryClass" and "extraFrame". Both need a fully qualified class name of the class that will be used for creating an additional frame. Though it really depends on the SDK version you're using as in some versions (sorry, don't remember exactly) SystemManager checks if there are more than 2 frames and will throw an error if there are more and won't probably initialize the application... there's also a -frame <label> <frameClass> additional compiler argument that should let you add one more frames to the SWF (so that you can put your resources there), but, again, it won't work with all the SDK versions (probably, most of them...)
So, it would be either a lot of hacking to be done, or you will have to agree to the concept of Flex preloader and load all your resources on frame 2...
justme123
04-30-2009, 02:36 PM
Thanks for your answer, i'll have a look into that. too bad its undocumented and not officially supported.
Reading your post gave me another idea though. Would it be possible to implement my own custom preloader that loads the user interaction first and bigswf after the interaction was done?
Or move the user interaction into the custom preloader itself?
wvxvw
04-30-2009, 03:27 PM
If you may have more than one SWF, then sure, you can load nearly any external resource at any time you want :)
It just some times the sites that host flash games don't like SWFs that require loading external data, I thought it was the case.
justme123
04-30-2009, 05:29 PM
you are right, the goal is to have only one .swf file that contains the user interaction and the big content .swf.
But the content should not be loaded before the interaction has taken place, or at least the interaction should start before the content was downloaded completely.
Maybe my understanding of flex and progressive downloading is wrong, so here's my 0.02$ on it:
1. flex .swf have 2 frames
Frame 1 contains the SystemManager and a Preloader
Frame 2 contains the application
2. swf progressive downloading works on a frame by frame basis
for flex this means Frame 1 is downloaded and gets displayed, but does litte except showing a loading animation
The app starts after frame2 was completely downloaded.
And here's where my custom preloader idea kicks in:
If i can do user interaction in frame1, the user doesn't need to wait for frame2 to finish loading. Maybe (hopefully) i can even control if it gets loaded at all.
Does that sound possible?
wvxvw
04-30-2009, 09:19 PM
Yes, quite possible, I believe that if you implement some "IPreloader" (sorry, haven't looked what the requirements for preloader are, but should be something like that), you can make preloader into a "separate" application, though, you won't have a preloader for that first preloader :) so, you'll have to take care it's not to big.
http://livedocs.adobe.com/flex/3/langref/mx/core/Application.html#preloader
aktell
05-01-2009, 08:21 AM
Hi there,
Reading this threat I begin to wonder if ‘justme123’ would not be better of to use modules which would give him everything he is looking for and he does not necessarily have to look at embedding his .swf file as the loading of his file would be done in the background while the application is starting up and visitor is in the authentication process.
I mean if we would know round about the file size of the big file that would help! If it comes push to chuff he could use mutable modules which download in stages right from the start using the Module Loader / Preload etc. ???
regards aktell
justme123
05-04-2009, 08:46 AM
bigfile.swf can be larger than 10mb and may be less than 1mb in some cases.
Modules sound nice, but wouldn't that be 2 distinct .swf files again?
I'll dig into this some more and keep you informed, thank you for your input.
wvxvw
05-04-2009, 09:01 AM
Just thought of something much simpler :)
Compile your SWF as you did before, i.e. with the bigfile.swf embedded on the second frame, then make a pure AS3 project, let it have 2+ frames, embed the flex-two-frame.swf on the second / last frame, then you can run a preloader "in the background" while interacting with user on the first frame.
If you need a non-framework (pure AS3) template for two-frame SWF, look here:
http://www.flashdevelop.org/community/index.php
(this is an alternative code editor, it's written in .NET, so, it's only for Win / Parallel) It has project templates, you'll need the "AS3 Project with preloader" template.
Or else, you may look into this my little project:
http://code.google.com/p/e4xu/source/browse/#svn/trunk/src/view
(this may be quite some work for you, as it is quite experimental at this point, though, I have a working test project, which works:
http://e4xu.googlecode.com/files/frameworkless.zip - the example project
http://e4xu.googlecode.com/files/framework.swc - the patched framework.swc )
If you'll be interested to use my project, and will have problems, let me know, I'll see if I can fix / change it.
justme123
05-05-2009, 08:59 AM
wow, thanks for all your pointers... sadly i have to get other things done first now. But i'll return to this soon and keep you updated.
justme123
05-20-2009, 04:03 PM
I'm back on this issue now and am trying to create a custom preloader.
To add a custom Preloader, you set the preloader property of application to a class that implements IPreloaderDisplay.
Example implementations i've found on the web all extend DownloadProgressBar or Sprite and to minimal drawing with graphics (to keep the Preloader as small as possible and display instantly). As that's not enough for the kind of user interaction i'm after, i want to use Flex components (at the expense of waiting for flex to load). But when i use a Flex Container as base, it doesn't display at all :/.
I think i'm missing something obvious, but havn't found a solution yet. Any idea how i need to setup the custom preloader to act just like a flex app?
In my tradition of finding a clue right after asking for help, i have found that obvious issue: When i add 'systemManager.addChild(this);' in line 46 of CustomPreloader (see below), it shows the button i use as an example.
I'm sure there is a more standard way to do it though, so any pointers would be great.
Here's my updated example code:
MyCustomFlexPreloader.mxml
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" preloader="CustomPreloader">
<mx:Label text="Hello Loaded World!"/>
</mx:Application>
CustomPreloader.as
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.events.ProgressEvent;
import mx.containers.Canvas;
import mx.controls.Button;
import mx.events.FlexEvent;
import mx.preloaders.IPreloaderDisplay;
public class CustomPreloader extends Canvas implements IPreloaderDisplay {
private var _preloader:Sprite;
public function CustomPreloader()
{
super();
}
public function set preloader( preloader:Sprite ):void
{
this._preloader=preloader;
preloader.addEventListener( ProgressEvent.PROGRESS , SWFDownloadProgress );
preloader.addEventListener( Event.COMPLETE , SWFDownloadComplete );
preloader.addEventListener( FlexEvent.INIT_PROGRESS , FlexInitProgress );
preloader.addEventListener( FlexEvent.INIT_COMPLETE , FlexInitComplete );
}
private function SWFDownloadProgress( event:ProgressEvent ):void {
var i:int=0;//just for breakpoints
}
private function SWFDownloadComplete( event:Event ):void {
var i:int=0;//just for breakpoints
}
private function FlexInitProgress( event:Event ):void {
var i:int=0;//just for breakpoints
}
private function FlexInitComplete( event:Event ):void
{
//flex should be available now, inizialize self
super.initialize();
systemManager.addChild(this);//with this the button shows up
//add interactive component
var button:Button = new Button();
button.label = "hello interactive preloading world";
button.addEventListener(MouseEvent.CLICK,buttonCli ck);
button.x = 50;
button.y = 50;
addChild(button);
}
private function buttonClick(event:MouseEvent):void{
//dispatch complete event to finish preloading
dispatchEvent(new Event(Event.COMPLETE));
}
//satisfy IPreloaderDisplay
override public function initialize():void{
//is called by SystemManager right away, override it to prevent using flex before it is initialized
//call super.initialize() once flex has loaded
}
//uninteresting IPreloaderDisplay implementation follows
private var _backgroundAlpha:Number = 1;
public function get backgroundAlpha():Number
{
if (!isNaN(_backgroundAlpha))
return _backgroundAlpha;
else
return 1;
}
public function set backgroundAlpha(value:Number):void
{
_backgroundAlpha = value;
}
private var _backgroundColor:uint;
public function get backgroundColor():uint
{
return _backgroundColor;
}
public function set backgroundColor(value:uint):void
{
_backgroundColor = value;
}
private var _backgroundImage:Object;
public function get backgroundImage():Object
{
return _backgroundImage;
}
public function set backgroundImage(value:Object):void
{
_backgroundImage = value;
}
private var _backgroundSize:String = "";
public function get backgroundSize():String
{
return _backgroundSize;
}
public function set backgroundSize(value:String):void
{
_backgroundSize = value;
}
private var _stageHeight:Number = 100;
public function get stageHeight():Number
{
return _stageHeight;
}
public function set stageHeight(value:Number):void
{
_stageHeight = value;
}
private var _stageWidth:Number = 100;
public function get stageWidth():Number
{
return _stageWidth;
}
public function set stageWidth(value:Number):void
{
_stageWidth = value;
}
}
}
wvxvw
05-20-2009, 11:13 PM
I'm afraid you cannot extend / use UIComponent based components for preloader as SystemManager overrides getDefinitionByName and pretends as those classes are loaded while on the first frame, even though they are not...
Sorry to say, this is what the developers of Flex framework call loading speed optimization... *sigh*
But I still think that there is a way to solve it by not using any of Flex stuff on the first frame like this:
- compile your flex application as you'd normally do.
- create pure AS3 project.
- create 2 classes, first for preloader, second for the main application.
- embed the entire Flex application in the second class.
- design the preloader as you want, but make it wait for the second frame to load in background (have an enterFrame listener that'll check if the second frame is loaded).
- once the second frame is loaded allow user to advance to the second frame.
justme123
05-22-2009, 04:19 PM
Basically i need/want flex for the user interaction (authentication) because (re)implementing WebService, Form etc. doesn't sound reasonable to me.
Given the time i spent on this problem, my opinion might change soon though.
Here's a bug about adding a 3rd frame with the -frame compiler option: http://bugs.adobe.com/jira/browse/SDK-19333 that i've run into today aswell.
SystemManager line 3147,3148
var c:Class = Class(getDefinitionByName(frameList[currentLabel]));
c["frame"](this); //fails with 'value is not a function'
I don't know what the SystemManager expects the class to look like, simply adding a "frame(sm:SystemManager)" function didn't do it.
I guess i'll end up using 3 frames:
one: pure actionscript preloader (main application for compiler)
two: flex app with user interaction
three: content frame
Now i just have to find out how to do that the 'right' way.
Or maybe i convince the powers that be that a one .swf solution isn't flexible enough ;)
thanks again wvxvw for your input. I really apreciate it.
wvxvw
05-22-2009, 05:45 PM
If the WebService is what concerns you, you can look here:
http://code.google.com/p/e4xu/source/browse/#svn/trunk/src/org/wvxvws/net
(these are severl classes I've written to substitute <mx:HTTPService> and <mx:RemoteObject>. However these haven't been tested to much, and have less functionality then mx.* classes, should be light-weight :)
Also, you may want to look here:
http://code.google.com/p/e4xu/source/browse/#svn/trunk/src/org/wvxvws/base
(these the AS classes)
and here:
http://code.google.com/p/e4xu/source/browse/#svn/trunk/src/view
(these are MXML that extend previous classes)
them both intended for creating multiple frames SWFs.
justme123
05-25-2009, 04:26 PM
Currently i use flexbuilder's data->import webservice to generate webservice classes for me with the wsdl. Styling with css and the visual component designer are also nice to have, so getting it to work with flex is worth it for me.
I got it working now, after hitting another bug in flex 3.3: http://bugs.adobe.com/jira/browse/SDK-20963
Loading a flex swf with Loader does work in 3.2, but not in 3.3. Also using SWFLoader to load a flex swf in 3.3 doesn't work. But i need 3.3 for the webservice (a bug/missing feature in 3.2 causes the generator to fail for our wsdl).
Solution:
create an actionscript project targeting flex sdk 3.2 as wrapper project and embed a flex 3.3 swf inside.
I'll attach demo code tomorrow.
wvxvw
05-25-2009, 07:26 PM
http://www.actionscript.org/forums/showpost.php3?p=864305&postcount=15
This is how you load Flex framework SWF if you need.
Besides... erm... c'mon, is it such a big deal to write WSDL + adjust your AS3 service classes by hand that you're using the data wizard? No, I really want to know, because I did post a lot about how this wizard is a junk and noone needs it :)
Oh, and IMO having 2 different frameworks inside a single SWF is well... I wouldn't do it... not because I have any specific bug to report, but, I am sure it will give you even more bugs later on the framework level that will be more difficult to solve than those you have now.
Not to sound paranoic, I would never rely on Flex framework, especially in problematic areas... And, considering when this thread had begun, I'd say that if you started writing it in pure AS3 back at where the thread had started you'd have your preloader frame now finished and forgotten... ;)
justme123
05-26-2009, 12:52 PM
i did not work on this nonstop, but you are right it took too long. I guess it totals to about 4 days of work to 'solve' this issue for me. I'm quite positive that it would have taken longer to rewrite the flex app into pure actionscript though(remember, i'm still quite new to AS and flex as a whole).
And while i agree with you that getting rid of flex altogether could be a good idea, it's in for now, as creating the ui in flex is easier.
A similar argument can be made for the wsdl and generator. Sure i can adapt wsdl changes manually. But as that wsdl is still in flux, this causes additional work with each change.
Using the code you linked (thanks again!) i was able to do it in flex 3.3 only, though i don't really understand why, as the code that triggered bug http://bugs.adobe.com/jira/browse/SDK-20963 did set applicationDomain aswell.
Here's the promised sample code, for use in an ActionScript Project with -frame=two,FlexWrapper compiler option
ActionScriptMain.as,
package {
import flash.display.DisplayObject;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.events.Event;
import flash.utils.getDefinitionByName;
public class ActionScriptMain extends MovieClip
{
public function ActionScriptMain()
{
stop();
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
addEventListener(Event.ENTER_FRAME,onEnterFrame);
}
private function onEnterFrame(e:Event):void{
switch(currentFrame){
case 1:{
if(framesLoaded>1){
nextFrame();
var flexWrapperClass:Class = Class(getDefinitionByName("FlexWrapper"));
var flexWrapper:Sprite = new flexWrapperClass() as Sprite;
addChild(flexWrapper as DisplayObject);
}else{
//wait, display progress, whatever
}
}break;
case 2:{
removeEventListener(Event.ENTER_FRAME,onEnterFrame );
}break;
default:{
}
}
}
}
}
FlexWrapper.as
package
{
import flash.display.Loader;
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.Event;
import flash.system.ApplicationDomain;
import flash.system.LoaderContext;
public class FlexWrapper extends Sprite
{
[Embed (source='flex.swf',mimeType="application/octet-stream")]
private var embeddedFlex:Class;
private var loader:Loader;
public function FlexWrapper()
{
loader = new Loader();
var context:LoaderContext = new LoaderContext(false, ApplicationDomain.currentDomain);
loader.loadBytes( new embeddedFlex(), context );
loader.contentLoaderInfo.addEventListener(Event.CO MPLETE,loaded);
}
private function loaded(e:Event):void{
addChild(loader);
loader.content.addEventListener(Event.ENTER_FRAME, appComplete);
}
private function appComplete(e:Event):void{
if((loader.content as MovieClip).application){
loader.content.removeEventListener(Event.ENTER_FRA ME,appComplete);
//loader.content as MovieClip).application is a reference to the flex app, you can add event listeners etc.
}
}
}
}
chained loading is possible by using additional -frame arguments and adding more case options in the onEnterFrame handler of ActionScriptMain.
Peter Cowling
05-26-2009, 01:13 PM
Just read this through, and trying to work out what you get from this approach that you cannot from (flex) Modules.
The only thing I can see is that you externalising the flex framework from your published .swf, and therefore defer your frame1 load to frame2.
Interested to know...
justme123
05-26-2009, 02:06 PM
the app that uses this approach has 3 frames
1. ActionScriptMain
2. FlexWrapper
3. AnotherWrapper
The content of frame3 is downloaded via progressive download while the content of frame2 is shown. It switches to frame3 once it has finished loading and the user interaction in frame2 is complete.
This is different from flex default, where all embedded content goes to frame2 and has to be downloaded before the application even starts.
From what i understand, flex modules are different swfs loaded at runtime with relative url's, but the app should be a single self contained .swf file.
Peter Cowling
05-26-2009, 02:23 PM
Okay, with you; I did not pick up on your 'chained loading is possible etc' bit below. Thanks.
|
vBulletin® v3.8.4, Copyright ©2000-2009, Jelsoft Enterprises Ltd.