Titanium MapView不释放内存

时间:2011-06-30 17:15:13

标签: ios memory-management mobile titanium

我有一个使用Appcelerator Titanium构建的应用程序(适用于iOS,最新的Ti和Apple SDK),其中一部分很大程度上依赖于地图。我遇到的问题是,当我关闭包含MapView的窗口时,内存似乎没有释放。因此,从菜单屏幕来回移动到地图会减慢iPhone的速度,直到它最终完全停止响应(3-5个地图加载)。

我使用Titanium的Ti.Platform.availableMemory调用在使用地图进入窗口时查看内存,并在地图关闭后使用。结果是每个连续进入/退出的稳定下降趋势,沿着以下方向:

25(map.js的初始加载)
20(注释后)
20(在win.close()之后)
19(map.js的第二次加载)
18(注释)
19(离开)
18(输入)
16(注释)
15(离开)

在模拟器中,当窗口关闭时,它可能会上升一点,但即使它显示稳定的下降趋势。

这是地图的代码,它位于自己的“map.js”文件中。我已经将其修改为使用的功能代码(因此为什么只有button_index的事件监听器在这里)。

button_index.addEventListener('click', function()
{
    Ti.App.xhr.abort();
    if (mapview) {
        mapview.removeAllAnnotations();
        mapview = null;
    }
    if(policeJson){
        policeJson = null;
        fireJson = null;
    }
    Ti.App.police = false;
    Ti.App.types = null;
    win.close(); //This should clean up everything, according to the docs
    Ti.API.info('Memory: ' + Ti.Platform.availableMemory);
});

var mapview;// = Ti.App.mapview;

Titanium.Geolocation.purpose = "Recieve User Location";
Titanium.Geolocation.accuracy = Titanium.Geolocation.ACCURACY_BEST;
Ti.API.info('Memory: ' + Ti.Platform.availableMemory);

function getMarkers(e){
    var miles = Ti.App.miles;
    Ti.API.info("Getting markers");
    //Google's API radius is in meters, so we need to convert
    var radius = miles * 1610; // 1mi = 1609.344 meters, so we just round up to 1610.
    // http connection setup
    Ti.App.xhr.setTimeout(10000);

    googleLatLng = e.coords.latitude + "," + e.coords.longitude;

    Ti.App.xhr.onload = function()
    {
        var data = Ti.XML.parseString(this.responseText);

        var ref = data.documentElement.getElementsByTagName("reference");
        if(ref != null && Ti.App.xhr.readyState == 4){
            for(var i =0; i < ref.length; i++){
                var marker = new Object();
                marker.lat = data.documentElement.getElementsByTagName("lat").item(i).text;
                marker.lng = data.documentElement.getElementsByTagName("lng").item(i).text;
                marker.name = data.documentElement.getElementsByTagName("name").item(i).text;
                marker.ref = ref.item(i).text;
                addMarker(marker);
                marker = null;
            }
        }
    };
    Ti.App.xhr.open("GET","https://maps.googleapis.com/maps/api/place/search/xml?location=" + googleLatLng + "&radius=" + radius + "&types=" + Ti.App.types + "&sensor=true&key=" + Ti.App.apiKey,false);

    Ti.App.xhr.send();
    Ti.API.info('Memory: ' + Ti.Platform.availableMemory);
}

// find the user's location and mark it on the map
function waitForLocation(e)
{
    var region = null;
    if ( e.error ) {
        region = regionDefault; // User didn't let us get their location
        var alertDialog = Titanium.UI.createAlertDialog({
            title: 'Geolocation',
            message: 'We were unable to center the map over your location.',
            buttonNames: ['OK']
        });
        alertDialog.show();
    } else {
        region = {
            latitude: e.coords.latitude,
            longitude: e.coords.longitude,
            animate:true,
            latitudeDelta:0.05,
            longitudeDelta:0.05
        };
    }
    Ti.App.lat = region.latitude;
    Ti.App.lng = region.longitude;
    mapview.setLocation(region);

    mapview.removeAllAnnotations();
    currentLoc = Titanium.Map.createAnnotation({
        latitude: region.latitude,
        longitude: region.longitude,
        title: e.error ? "Columbus" : "You are here!",
        pincolor: Titanium.Map.ANNOTATION_RED,
        animate:true
    });
    mapview.addAnnotation(currentLoc);
    mapview.selectAnnotation(currentLoc);
    mapview.addEventListener('click', function(e){
        if (e.clicksource == 'rightButton') {
            if (e.annotation.spotUrl != '') {
                alert('Website!');
            }
            else {
                alert('No website available');
            }
        }
    });

    if(Ti.App.police == true) {
        var fire_img = "../../images/iNeighborhood/fire.png";
        var police_img = "../../images/iNeighborhood/police.png";
        serviceMarkers(fire_addr, fire_title, fire_lat_1, fire_long_1,fire_img);
        serviceMarkers(police_addr, police_title, police_lat_1, police_long_1,police_img);
    }

    getMarkers(e);
}


function addMarker(marker){
    var ann = Titanium.Map.createAnnotation({
        animate:true,
        latitude:marker.lat,
        longitude:marker.lng,
        title:marker.name,
        pincolor: Titanium.Map.ANNOTATION_GREEN
    });

    mapview.addAnnotation(ann);
}

// Automatically refresh current location.
/*
 * IN PROGRESS
 */
function getLocation(){
    // create the mapView and center it on Columbus
    if (!mapview) {
        mapview = Titanium.Map.createView({
            mapType: Titanium.Map.STANDARD_TYPE,
            animate: true,
            region: {
                latitude: 39.961176,
                longitude: -82.998794,
                latitudeDelta: 0.1,
                longitudeDelta: 0.1
            },
            regionFit: true,
            userLocation: true,
            visible: true,
            top:29
        });

        //Ti.App.mapview = mapview;

        win.add(mapview);
    }
    refresh();

    //Get the current position and set it to the mapview
    Titanium.Geolocation.getCurrentPosition(waitForLocation);
}
getLocation();

// pretty self explanatory...
function cleanMap(){
    if (mapview) {
        mapview.removeAllAnnotations();
    }
    if(xhr){
        xhr.abort();
    }
}

Ti.App.addEventListener('map:mapIt',function(){
    cleanMap();
    getLocation();
});

以下是加载地图的索引页面中的一些代码:

var winMap = Titanium.UI.createWindow({
    url:'map.js',
    tabBarHidden:false
});

btnEducation.addEventListener('click',function(){
    Ti.App.types = Ti.App.schools;
    Ti.UI.currentTab.open(winMap);
    Ti.App.police = false;
});

我创建了一个全局HTTPClient并重复使用它,就像其他一些Q&amp; A答案(在SO和Appcelerator的网站上)所提出的那样,这似乎有所帮助(没有多少内存耗尽每个地图加载),我也尝试手动将变量(尤其是较大的变量)设置为null(可能有效也可能无效),但仍有一些东西仍在继续。我也尝试在事件监听器中为打开窗口的按钮创建地图窗口,但这似乎没有任何影响。

我还运行了仪器,看看它能找到什么并且没有发现任何值得注意的东西(我甚至向同事展示了它,他全职做移动开发,他说没有什么不同寻常的他可以看到。)

我一直在看这段代码几个小时,而且这不是我的所有代码,所以我完全有可能错过一些明显的东西,但是我的代码中有一个原因导致内存不存在应该发布吗?我还能做些什么才能获得更多内存才能发布?我现在只为iOS开发,因此iOS专用解决方案是可以接受的。

修改 - 我现在也尝试将地图部分包含在调用它的文件中(使用Ti.include('map.js'))。我做了一个快速而肮脏的设置,看看它是否可行:

Ti.include('map.js');
var button_index = Ti.UI.createButton({
   text:'Back',
   height:20,
   width:50,
   top:0,
   left:0,
   color:'#000'
});
button_index.addEventListener('click', function()
{
    Ti.App.xhr.abort();
    if (mapview) {
        mapview.removeAllAnnotations();
//        mapview = null;
    }
    if(policeJson){
        policeJson = null;
        fireJson = null;
    }
    Ti.App.police = false;
    Ti.App.types = null;
    Ti.App.title = null;
    mapview.hide();
    Ti.API.info('Memory: ' + Ti.Platform.availableMemory);
});
mapview.add(button_index);
mapview.hide();

btnArts.addEventListener('click',function(){
    Ti.App.types = Ti.App.arts;
//    Ti.UI.currentTab.open(winMap);
mapview.show();
Ti.App.fireEvent('map:mapIt'); //Triggers the chain of events to clear the map and add the necessary annotations to it
    Ti.App.police = false;
    Ti.App.title = 'arts';
});

它似乎运行得更好,但是当我进出mapview时,可用内存量仍然稳定下降,并且初始内存负载使其在设备上与其他方法一样无法使用(掉线)内存低至约3MB)。

4 个答案:

答案 0 :(得分:1)

从有关选项卡/选项卡组的文档...“TabGroup选项卡实例。每个选项卡实例都维护一组选项卡窗口。一次只能看到选项卡中的一个窗口。当窗口关闭时,无论是由用户还是通过代码,窗口都从堆栈中移除,使前一个窗口可见。“

有一种猜测是,close()应用于选项卡时可能不会按照您的假设行事,因为它似乎在标签之间保持状态,因为它们在它们之间循环。此外,上面的代码示例中可能缺少某些内容,但实际上并没有看到哪里 “win”被定义为一个变量(我假设你在某个地方有var win = Ti.UI.currentWindow();,但是你可能想要在调用该函数时仔细检查它是否真的被关闭了。

您还可以考虑为应用程序创建单个对象,并将函数链接到该对象,以免污染全局范围。请参阅:http://wiki.appcelerator.org/display/guides/JavaScript+Best+Practices

答案 1 :(得分:0)

每次返回地图时,是否会调用winMap,其“重窗口”(即指向URL并创建另一个js上下文,而不是包含在相同的上下文中)?我没看到从哪里打电话。

答案 2 :(得分:0)

你确定要解除分配mapView的内存吗?我看到代码的预感是它可能是罪魁祸首。

我可能会建议使用一个全局mapView对象,而不是继续在其中创建map.js

答案 3 :(得分:0)

如果您将来有更多项目在Titanium中创建,我只想添加,建议您设置应用程序,以最大限度地减少内存问题。

首先,我不建议您使用Ti.include()函数。还有一个更好的替代方法叫做require()。

我有很多问题没有正确收集垃圾,但是这些链接帮助我编写了内存高效的应用程序:

这是从Appcelerator: http://search.vimeo.com/29804284#

这解释了require函数和CommonJS模块: https://wiki.appcelerator.org/display/guides/CommonJS+Modules+in+Titanium

如何使用CommonJS的示例: https://github.com/appcelerator/Documentation-Examples/blob/master/commonjsExample/Resources/modules/pages/userlist.js

我希望这有帮助!