Sunday, August 22, 2010

Preloading External SWF File Generates Error #1009: Cannot access a property or method of a null object reference

Exercise Files:
Stage_Issue_Preloader.fla
Stage_Issue_Load_This.fla

When you're preloading a SWF file that has code that makes any reference to the stage, you might encounter this error:

TypeError: Error #1009: Cannot access a property or method of a null object reference.

Let's take a look at the sample files and see if we'll get that same error. Make sure that you've downloaded them and placed them in the same folder. We have 2 files:
  1. Stage_Issue_Preloader - this Flash movie will be used to preload the other file
  2. Stage_Issue_Load_This - this Flash movie will be the one that's preloaded into the preloader movie
Both files already have code in them.

Let's open the Stage_Issue_Load_This.fla file first. Go ahead and test the movie so that Flash will generate a SWF file inside the same folder that contains our Flash documents. What happens in this movie is when you hit the enter key on your keyboard, the smiley face in the center of the stage will rotate. Try it out, but make sure that you disable keyboard shortcuts by going to Control > Disable Keyboard Shortcuts. What the movie does is not really important. What's important here is that if you take a look at the code in frame 1 of the Actions layer, you will see a reference to the stage at the beginning of the code:
stage.addEventListener(KeyboardEvent.KEY_DOWN, rotateSmileys);
So just take note of this line. Although it does not seem to be causing any errors now, it will once we try to preload it into the preloader file. So let's try that.

Open the Stage_Issue_Preloader.fla file. Check the code on frame 1 of the Actions layer and you will see that we already have our basic preloader code that will preload the Stage_Issue_Load_This.swf file. Now test the movie and you should see that same error message I mentioned at the beginning of this article:

TypeError: Error #1009: Cannot access a property or method of a null object reference.

This error mentions a null object. When you say null object, it means that the object has no value and, in a way, is kind of like a non-existent object. And the null object being referred to here is actually the stage.

But how can the stage be null? Isn't there already a stage when the preloader file is launched?
Yes. But the problem is that stage can only be accessed from a display object that's already in the display list. Our stage reference (the line that says stage.addEventListener(KeyboardEvent.KEY_DOWN, rotateSmileys);) is inside the Stage_Issue_Load_This.swf file. That's the file that's being preloaded, and it won't get added to the display list until it has loaded completely. So essentially, this means that for a certain amount of time while it's being loaded, it actually won't have a stage. So that's why stage is being seen as null. You see, when you're loading an external SWF file in Flash, it can actually start reading the code inside that SWF file even though it hasn't completely loaded yet. So in this circumstance, what happens is that the external SWF file hasn't been completely loaded yet, but Flash is already trying to execute the code inside it. So it's trying to execute the stage.addEventListener... line, but as we've learned, the external SWF file does not have a stage yet. But once it's been loaded completely and has been added to the display list, then the stage reference inside the Stage_Issue_Load_This.swf file will no longer be null.

So how do we fix this?
We'll take a look at 3 ways in which we can fix this issue. Basically, what we want to do is to make sure that stage is not null when the stage.addEventListener... line gets executed.


Method #1
For the first method, we will be adding some code to the Stage_Issue_Load_This.fla file (that's the file that's going to be preloaded). So make sure that file is open, then select frame 1 of the Actions layer and then go to the Actions panel. What we'll do is we will add an event handler that will tell us when the Flash movie has been added to the stage. The event for that is Event.ADDED_TO_STAGE. This event gets dispatched by the display object whenever it's been added to the stage. So when our Flash movie gets added to the stage, only then will we let Flash execute the stage.addEventListener... line. So we'll create an Event.ADDED_TO_STAGE event handler, and then place the stage.addEventListener... line inside the event listener function associated with that event handler.
this.addEventListener(Event.ADDED_TO_STAGE, checkStage);

function checkStage(e:Event):void
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, rotateSmileys);
this.removeEventListener(Event.ADDED_TO_STAGE, checkStage); //Don't forget to remove the ADDED_TO_STAGE event listener after the stage.addEventListener... line has been added already
}
So here, we have an event listener function named checkStage that get's called when the Flash movie has been added to the stage. Once it's called, only then will the stage.addEventListener... line be executed. And since the Flash movie has already been added to the stage, then we won't get the previous error anymore.

*Make sure that you test this movie first in order to generate the SWF file with the updated code before you test it with the preloader movie.

The problem with this method, though, is that the movie won't work properly if you run it by itself (meaning when you don't load it using the preloader file). When you run the Flash movie by itself, then the Event.ADDED_TO_STAGE event never gets dispatched. So the stage.addEventListener... line never gets executed. The Event.ADDED_TO_STAGE event doesn't get dispatched because when you run the Flash movie by itself, it never adds itself to its own stage. It has to be loaded by another Flash movie and then be added to that Flash movie's stage, in order for the Event.ADDED_TO_STAGE event to be dispatched. So let's go ahead and take a look at other methods as well.


Method #2
For this second method, we will be adding some code to the Stage_Issue_Load_This.fla file as well (that's the file that's going to be preloaded). So make sure that file is open, then select frame 1 of the Actions layer and then go to the Actions panel. What we'll do is we will add some sort of checker that will check whether stage is not equal to null (if stage is not equal to null, then this means that our external SWF file already has a stage). If the stage is still null, then don't execute the code. But if the stage is no longer null, then Flash can go ahead and execute the stage.addEventListener... line. So let's take that stage.addEventListener... line and put it in an if statement that checks if stage is not equal to null.
if(stage != null) {
stage.addEventListener(KeyboardEvent.KEY_DOWN, rotateSmileys);
}

But this is not quite complete yet. This if statement will only run once. We want it to keep running so that it's going to keep on checking until stage is no longer equal to null. And one way of making this if statement execute numerous times would be to put it in an ENTER_FRAME event handler.
this.addEventListener(Event.ENTER_FRAME, checkStage);

function checkStage(e:Event):void
{

if(stage != null)
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, rotateSmileys);
}

}
So here, I've created an ENTER_FRAME event handler that calls the listener function named checkStage. And inside the checkStage listener function is the if statement that checks if stage is not equal to null. And since this is an ENTER_FRAME event handler, this function is going to run numerous times. Therefore, the if statement inside it will keep checking if stage is no longer null. So we've ensured that only then will the stage.addEventListener... line be executed, thus avoiding the error.

And also, let's not forget to remove the ENTER_FRAME event listener once stage is no longer null, since by that time, we no longer need to check for that condition. If we don't remove the listener then it will keep on running and needlessly take up computing resources.
this.addEventListener(Event.ENTER_FRAME, checkStage);

function checkStage(e:Event):void
{

if(stage != null)
{
stage.addEventListener(KeyboardEvent.KEY_DOWN, rotateSmileys);
this.removeEventListener(Event.ENTER_FRAME, checkStage);
}

}
*Make sure that you test this movie first in order to generate the SWF file with the updated code before you test it with the preloader movie.


Method #3
In this third method, the code will be added to the Stage_Issue_Preloader.fla file instead. This fix is much shorter. You'll only need to transfer an already existing line to the top part of the code. But before we add the code, let's take a quick look at how a loader object works:
When an external SWF file is loaded by a loader object, the SWF file gets loaded as a child of the loader object. This is another way of saying that the external SWF file actually goes inside the loader. Once loading is complete, we can then add the loader to the stage. And since the loaded SWF file is inside the loader, then the SWF file gets added to the stage along with it. Just keep that in mind as the fix in this method has something to do with that.

So now, let's go ahead and edit the code. Make sure that the Stage_Issue_Preloader.fla file is open, then select frame 1 of the Actions layer and then go to the Actions panel. Scroll down until you see the function named loaded.
function loaded(e:Event):void
{
removeChild(percent_txt);
removeChild(progressBar_mc);
addChild(myLoader);
}
This function named loaded is the listener function for the Event.COMPLETE event handler in the code. This function will get called only when the loader object has finished loading the external SWF file. It is in this function where we add the loader object named myLoader to the stage. So this means that the loader object only gets added to the stage at the end of the loading process. But we can actually add the loader earlier. We can add it to the display list immediately after we create it, and before we even start the loading process. Instead of having the addChild(myLoader); statement inside the event listener function, let's just place it after we instantiate the loader object.
var myLoader:Loader = new Loader();
var myURL:URLRequest = new URLRequest("Stage_Issue_Load_This.swf");

addChild(myLoader);
This way, the loader gets added to the stage immediately. It will be empty at first, but it will already be on the stage. And that's OK. You can place empty loader objects on the stage. It does not have to have content in order for it to be placed on the stage.

So why do we need to add the
loader before we start the loading process? Well, because if the loader is already on the stage, it means that the externally loaded SWF file gets added to the stage right away as well (since we know that the SWF actually goes inside the loader). And since it's added to the stage right away, this means that stage will not be null.

If in the first and second methods, we made it so that the Flash movie will wait for stage to NOT be null before it executes the stage.addEventListener... statement, here, we've made it so that the external SWF file has a stage right away when it starts getting loaded into the Flash movie.

You can choose any of the methods, and hopefully, it will fix this issue for you.

8 comments:

  1. You are a life saver! This post is AWESOME!

    ReplyDelete
  2. THANK YOU!!! After scouring the net for days and hours, this is the first page that actual broke down the error and how to fix it step by step -- in a way I could understand. I've bookmarked this page. Thank you again!

    ReplyDelete
  3. This comment has been removed by the author.

    ReplyDelete
  4. You're welcome! You'll find more ActionScript tutorials here: AS3 Tutorials.

    Feel free to share it with others! Happy ActionScripting! :)

    ReplyDelete
  5. Hi, do you have any tutorial about getting this #1009 error because of elements INSIDE a FLA?
    Thanks

    ReplyDelete
  6. Yes! You're the life saver :) Thank you so much!

    ReplyDelete
  7. This is a one-stop tutorial, i must say.

    You don't only teach people the basic how-tos but also error locating.
    I do think that it's one of your site's edge from other tutorial sites.
    keep up the good work!

    ReplyDelete