MovieClip Management

Im Laufe der Entwicklungen an der LDIC-Offline-Präsentation bin ich auf einen Fehler in der Flash-IDE gestoßen, der mir so manche aufregende Stunden bereitet hat. Grund dafür war ein unerklärliches Phänomen, das die CPU-Last einer als Windows-Projektor exportierten Flash-Animation mit jedem Szenenwechsel Stück für Stück ansteigen ließ und den verwenden Host-Rechner bis an seine Grenzen trieb.

Das besonders Unerklärliche war, dass nur der Projektor von diesem Phänom betroffen war, die zu Grunde liegende Shockwave-Datei blieb davon unberührt wie dieses Bildschirmfoto beweist: Bug in Flash-Projekt lässt die CPU-Last steigen

Zu sehen ist die selbe Anwendung mit unterschiedlichen CPU-Auslastungen nach 5 aufgerufenen Seiten. Einmal als SWF im Standalone-Flashplayer (unten) und einmal als Projektor (oben).

Nach einer Reihe von Versuchen bin ich dann dahinter gekommen: Mit der Loader-Klasse geladene SWF-Dateien laufen auch nach vermeintlichem Entfernen im Hintergrund weiter. Der Garbage-Collector wird daran gehindert, den MovieClip und sein Eltern-Loader-Objekt aufzuräumen und aus dem Speicher zu entfernen.
Bestätigung für diesen Fehler fand ich auf dieser Seite "Garbage collector troubles using the loader class".

Weitere Nachforschungen ergaben, dass in AS3-exportiere (also in der selben Version wie die Host-Anwendung), SWFs dieses Problem nicht besaßen, zumindest nicht ganz so gravierend.
Mit einem Umschreiben (neu exportieren) der einzuladenden SWF in AS3 war das Problem minimiert, jedoch nicht ganz gelöst. Nun lief die CPU-Last nicht mehr bis über 90% an, dafür konnte man jedoch sehen, dass weiterhin ein Prozess im Hintergrund lief.
Ursache dafür sind in der SWF definierte EventListener und Interval-Aufrufe. Der GarbageCollector kann keine MovieClips entfernen die solche definiert haben. Damit es funktioniert, müssen diese manuell entfernt werden. Da ich die einzuladende SWF jedoch nicht von außen steuern wollte, behalf ich mir mit einem Trick innerhalb ihrer Hauptprozedur.

public function Constructor() {
    // Starte Zufallsgenerator der Balken
    interval1 = setInterval(zufallPeak, 1100);
    interval2 = setInterval(zufallPeak, 1639);
    // Starte Dekorationsanimation
    addEventListener(Event.ENTER_FRAME, runDecoration);
}

private function runDecoration(ev:Event):void {
    if (stage == null) removeMe();
}

private function removeMe():void {
    trace("remove Me");
    removeEventListener(Event.ENTER_FRAME, runDecoration);
    clearInterval(interval1);
    clearInterval(interval2);
}

Beim Entfernen des MovieClips mittels removeChild() aus der Anzeigeliste werden dessen Zeiger parent und stage auf NULL gesetzt. Auch wenn der Interval-Handler zufallPeak weiterläuft, das Objekt also nicht vollständig entfernt werden kann. Dieses Beispiel prüft in jedem ENTER_FRAME-Event, ob der Zeiger auf die Bühne noch existiert. Wenn nicht, werden die Intervalle beendet und Event-Handler entfernt. Das MovieClip kann nun vollständig vom GarbageCollector aufgeräumt werden, der Speicher wird frei und die CPU-Last bleibt niedrig.

Andere Blogbeiträge zum selben Thema:
Open Source Flash

Kommentare (1)


Mir ist gerade eingefallen, dass man das auch evt. mit den UNLOAD-Event der Loader.contentLoaderInfo realisieren könnte, wenn das Event bis zum Loader.content durchgebubbled werden kann. Da müsste mans aber auch von außen steuern bzw. ansprechen um die Handler zu verbinden.










Zurück zur Übersicht