更新XMLListCollection的有效方法

时间:2012-07-04 11:43:41

标签: xml actionscript-3 flex httpservice

我有以下代码:

private var xmlC:XMLListCollection = new XMLListCollection();
private var httpS:HTTPService = new HTTPService();
private var timer:Timer = new Timer(1000);
private var xmlData:XML;
private var xmlDataList:XMLList;

protected function application1_creationCompleteHandler(event:FlexEvent):void
{
    httpS.url = "data.xml";
    httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
    httpS.resultFormat="e4x";
    httpS.send();

    timer.start();
    timer.addEventListener(TimerEvent.TIMER, updateXMLC);
}

private function updateXMLC(event:TimerEvent):void
{
    xmlC.source = xmlDataList;
    httpS.send();
}

private function resultHTTP(event:ResultEvent):void
{
    xmlData = event.result as XML;
    xmlDataList = xmlData.dg.rows.row;
}

“data.xml”有5000行,所以我需要在必要时清理它的踪迹。由于分析

,我发现了两个问题
  1. 每次在方法updateXMLC中调用httpS.send()时,它都会在内部调用URLLoader,这样可以保留不再需要的XML而不会被垃圾回收而不会被垃圾回收
  2. 有更有效的方法来更新xmlC,每当更新XMLListCollection时,似乎XMLListCollection的前一个值都没有被垃圾收集

4 个答案:

答案 0 :(得分:4)

在查看代码后会有任何内存泄漏,我对此持怀疑态度/好奇心,所以我为自己测试了代码。

您发布的代码实际上无法运行。我刚刚添加了最小值,让它像app tag和import语句一样运行(以及一个计数器标签,以查看http服务周期何时完成):

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="application1_creationCompleteHandler(event)"> 
<mx:Script>
    <![CDATA[
        import mx.rpc.http.HTTPService;
        import mx.collections.XMLListCollection;
        import mx.rpc.events.ResultEvent;
        import mx.events.FlexEvent;

        private var xmlC:XMLListCollection = new XMLListCollection();
        private var httpS:HTTPService = new HTTPService();
        private var timer:Timer = new Timer(1000);
        private var xmlData:XML;
        private var xmlDataList:XMLList;

        protected function application1_creationCompleteHandler(event:FlexEvent):void
        {
            httpS.url = "data.xml";
            httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
            httpS.resultFormat="e4x";
            httpS.send();

            timer.start();
            timer.addEventListener(TimerEvent.TIMER, updateXMLC);
        }

        private function updateXMLC(event:TimerEvent):void
        {
            xmlC.source = xmlDataList;
            httpS.send();
        }

        private function resultHTTP(event:ResultEvent):void
        {
            counter.text = Number(parseInt(counter.text,10)+1).toString(); 
            xmlData = event.result as XML;
            xmlDataList = xmlData.dg.rows.row;
        }
    ]]>
</mx:Script>
<mx:Label id="counter" text="0" horizontalCenter="0" verticalCenter="0" fontSize="72"/>
</mx:Application>

好消息是代码中没有任何内存泄漏。在探查器中也没有任何游荡对象。

坏消息是你在应用程序中正在做的其他事情(这里没有发布的代码,无论出于何种原因而省略) - 这就是你有泄漏/游荡的对象。

您可以在附加代码的分析器中看到自己在http服务的前几个周期后内存不会增加。即随着时间的推移它不会继续抓住更多的记忆。 (顺便说一句,我提取的XML文件大约有8000行,超过1MB)。

如果你想发布更多代码,很高兴看得更远 - 但是现在想想这解决了这个谜。 ;)

以下是您正在做的事情的一些修复:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="application1_creationCompleteHandler(event)">
<mx:Script>
    <![CDATA[
        import mx.rpc.http.HTTPService;
        import mx.collections.XMLListCollection;
        import mx.rpc.events.ResultEvent;
        import mx.events.FlexEvent;

        [Bindable] private var xmlC:XMLListCollection = new XMLListCollection();
        private var httpS:HTTPService = new HTTPService();
        private var timer:Timer = new Timer(1000);
        private var xmlData:XML;
        private var xmlDataList:XMLList;
        private var serviceRunning : Boolean = false;
        private var currentData : String = '';

        protected function application1_creationCompleteHandler(event:FlexEvent):void
        {
            httpS.url = "data.xml";
            httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
            httpS.resultFormat="e4x";
            httpS.send();

            timer.start();
            timer.addEventListener(TimerEvent.TIMER, updateXMLC);
        }

        private function updateXMLC(event:TimerEvent):void
        {
            xmlC.source = xmlDataList;
            if( !serviceRunning ){  // don't call for more data until 
                httpS.send();       // you've gotten back last call
            }
        }

        private function resultHTTP(event:ResultEvent):void
        {
            // make sure we have differences before rebinding
            var newData : String = event.result as String;
            serviceRunning = false;
            counter.text = Number(parseInt(counter.text,10)+1).toString();
            if( newData !=  currentData ){
                xmlData = event.result as XML;
                currentData = newData;
                xmlDataList = xmlData.dg.rows.row;
            }
        }
    ]]>
</mx:Script>
<mx:Label id="counter" text="0" horizontalCenter="0" verticalCenter="0" fontSize="72"/>
 </mx:Application>

答案 1 :(得分:0)

如果我正确理解了您的代码,那么您正在从与SWF位于同一文件夹中的文件中恢复XML数据。如果是这种情况,那么最有可能发生的事情是XML文件正在浏览器中缓存,并且它会一遍又一遍地返回相同的缓存版本。

在处理这些问题时,您可以添加一个虚拟URL变量,使浏览器认为您要检索的文件是新的,并强制它再次恢复该文件。

您可以通过执行以下操作来实现此目的:

httpS.url = "data.xml?random="+Math.floor(Math.random()*1000);

如果这不能解决问题,那么可能与不重置保存实际XML数据的变量有关。您可以通过重置变量的新实例来完成此任务:

xmlData = new XML();
xmlDataList = new XMLList();
xmlC = new XMLListCollection();

或者,如果你想要的只是重置XMLListCollection,你可以尝试使用Flex中所有列表中包含的“refresh()”方法。

重要说明:如果您使用这些变量中的任何一个进行数据绑定,这可能会导致Flex无法对数据进行垃圾收集,因为它正在某处使用。

答案 2 :(得分:0)

请求,接收和处理XML需要多长时间?这听起来像是一个相当大的文件,所以如果它超过一秒钟,你将同时多次调用HTTPService(据我所知,你的当前计时器将尝试每秒发出一个请求,无论是否最后一个回来了。)

我不确定这是不是你想要的,但我对此表示怀疑。如果我是对的,并且你只想在得到最后一个请求之后发送请求,那么你可能最好摆脱计时器及其功能,然后将resultHTTP方法更改为类似这样:

private function resultHTTP(event:ResultEvent):void
{
    xmlData = event.result as XML;
    xmlC.source = xmlData.dg.rows.row;
    httpS.send();
}

当然,您可能希望每次都开始延迟:

    protected function application1_creationCompleteHandler(event:FlexEvent):void
    {
        httpS.url = "data.xml";
        httpS.addEventListener(ResultEvent.RESULT, resultHTTP);
        httpS.resultFormat="e4x";

        timer.start();
        timer.addEventListener(TimerEvent.TIMER, updateXMLC);
    }

    private function updateXMLC(event:TimerEvent):void
    {
        timer.stop();
        httpS.send();
    }

    private function resultHTTP(event:ResultEvent):void
    {
        xmlData = event.result as XML;
        xmlC.source = xmlData.dg.rows.row;
        timer.start();
    }

很抱歉,如果我误解了这个问题,那么......

无论哪种方式,你可能也希望将某种faultHTTP函数放入其中,因此它也会在HTTP错误上重新启动计时器(或做任何你需要做的事情)。

答案 3 :(得分:0)