Monkey patchers run into problems when using Flex RSLs. In this post I’ll explain why monkey patching does not work when using RSLs and provide details of how to make it work. I’ll monkey patch the mx.controls.Button class in this post and ListCollectionView and Spark Button in later posts.
Why doesn’t monkey patching work when using RSLs? To answer this question it is important to understand two things:
The Flash Player has a first-in-wins rule when loading classes.
Flex applications contain two frames. The first frame contains few classes and loads the RSLs. The second frame contains the rest of the application’s classes.
Say you’ve monkey patched mx.controls.Button, which will end up in frame 2 of your application. In frame 1 of the application, the framework RSL will be loaded which contains the mx.controls.Button class. When frame 2 of the application loads the monkey patched version of mx.controls.Button is ignored because of the Flash Player first-in-wins rule.
When you monkey patch a class that is in frame 1, then you are done, you don’t need an RSL.
The basic steps I’ll use to monkey patch are:
Create an RSL with just the monkey-patched file and its inheritance dependencies.
Add your RSL to the configuration to get it loaded.
To demonstrate the process I created a project with a simple application that contains one button.
<mx:Button label=”My Button”/>
The project was developed using a pre-release version of Flex4, build 22.214.171.12472.
Create an RSL
My modification of Button is just a change to set label() so I can see if a Button in my application is a real or patched Button. When the Button’s label setter is called I just prepend “Monkey patched ” to the original value.
_label = “Monkey patched ” + value;
I started by adding Button.as to my source tree using the same package as Flex, “mx.controls”. Next compile your source into an RSL. From the command line it will look something like this:
>compc -directory -output=button -link-report=link.rpt -include-classes=mx.controls.Button -source-path=.
>optimizer button/library.swf –output -button.swf
>digest button.swf -swc-path=button
After running these commands the size of button.swf is 143,596 bytes. Looking at the link report, there are a lot of classes besides mx.controls.Button that we don’t need. Ideally, all we want in the RSL is mx.controls.Button. But the Flash Player requires classes that Button extends or implements to be defined when the Button class is loaded. Since we are loading this RSL before the framework RSL, we need to also include the classes Button extends and implements in framework.swc. For each additional class we include, we also need to include the classes it extends and implements.
Take the link report from the first compile of Button and copy it to a file we will use as our externs file. The externs file is what we will use to remove classes we don’t want in the RSL. I actually seeded my externs.xml file by copying and pasting the script children from framework.swc’s catalog. My thinking was that I wanted to exclude everything in framework.swc except Button and the classes it extends or implements. But now I can see that the externs file is much larger than it would have been if I had just started with the initial link report.
Next edit the externs file by removing the script elements for mx.controls.Button and all of its inheritance dependencies. After recompiling I was able to get the RSL size down to 35,152 bytes. That is still pretty big but it’s the best we can do because we need UIComponent and UICompoment is big. When monkey patching spark components we will be able to remove UIComponent and get a much smaller RSL.
Add your RSL to the configuration
Now that you have your RSL, you just need to add it to the RSL configuration. I created a local configuration file, main-config.xml, with the RSLs I wanted. You can start by copying the <runtime-shared-library-path> elements from flex-config.xml to your local configuration file. Then add the entry for your RSL before the framework RSL.
<!– monkey patch mx.controls.Button from Framework SWC –>
The order of the runtime-shared-library-path options is important because it determines the load order of the RSLs. The RSL URLs in my configuration file are all local SWF files so before running the application I will copy the RSLs from my SDK frameworks/RSLs directory to where my application lives.
When the new RSL is loaded for the first time you will mostly encounter verify errors because you missed removing an interface from the externs file. As you encounter a verify error, remove that class from the externs file and try again. Eventually the RSL will contain all the classes it needs and load without errors.
Finally, run the application to verify the monkey patched Button is being used.
The postings on this site are my own and don’t necessarily represent Adobe’s positions, views, strategies or opinions.