Titanium MapView 不释放内存

发布于 2024-11-18 04:56:49 字数 7635 浏览 3 评论 0原文

我有一个使用 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 并重用它,就像其他一些问答答案(在 SO 上和 Appcelerator 网站上)所建议的那样,这似乎有所帮助(每次加载地图时消耗的内存并没有那么多),我还尝试手动将变量(尤其是较大的变量)设置为 null(这可能有效也可能无效),但有些东西仍然存在。我还尝试在事件侦听器内为打开窗口的按钮创建地图窗口,但这似乎根本没有任何效果。

我还运行 Instruments 来看看它能找到什么,但它没有发现任何值得注意的东西(我什至向我的同事展示了它,他全职从事移动开发,他说他没有看到任何不寻常的东西) 。

我已经查看这段代码几个小时了,这并不是我的全部代码,所以我完全有可能遗漏了一些明显的东西,但是我的代码中是否有原因导致内存没有被释放应该?我还能做些什么来释放更多内存吗?我现在只针对 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';
});

它似乎工作得更好,但是当我进出地图视图时,可用内存量仍然在稳步减少,并且初始内存负载使得它变得更好。与其他方法一样在设备上无法使用(将内存降至约 3MB)。

I have an app that's built using Appcelerator Titanium (for iOS, latest SDKs of Ti and Apple) and a section of it relies heavily on maps. The problem I'm having is that the memory doesn't seem to be releasing when I close the window containing the MapView. As a result, going back and forth from the menu screen to a map slows down the iPhone until it eventually stops responding altogether (3-5 map loads).

I used Titanium's Ti.Platform.availableMemory call to see the memory upon entering the window with the map, and after the map is closed. The result is a steady downward trend with each successive entry/exit, along the lines of:

25 (initial load of map.js)
20 (after annotations)
20 (after win.close())
19 (second load of map.js)
18 (annotations)
19 (leave)
18 (enter)
16 (annotations)
15 (leave)

In the simulator, it may go up a little more when the window's closed, but even it shows a steady downward trend.

Here's the code for the map, which is in it's own "map.js" file. I've trimmed it down to the functionality code that is used (hence why only the event listener for button_index is here).

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();
});

Here's some of the code from the index page that loads the map:

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;
});

I made a global HTTPClient and reuse it, like what some of the other Q&A answers (both on SO and on Appcelerator's site) have suggested, which seems to have helped (not as much memory is drained with each map load), I also tried manually setting the variables (especially the larger ones) to null (which may or may not be effective), but something's still holding on. I also tried creating the map window inside the event listener for the buttons that open the window, but that didn't seem to have any effect at all.

I also ran Instruments to see what it could find and it didn't find anything noteworthy (I even showed it to my coworker, who does mobile development full time and he said there wasn't anything out of the ordinary that he could see).

I've been looking at this code for a few hours now, and it's not all my code, so it's entirely possible I'm missing something obvious, but is there a reason in my code why the memory isn't being released as it should? Is there something else I could do to get more of the memory to release? I'm only developing for iOS right now, so iOS-specific solutions are acceptable.

Edit - I've now also tried including the map portion into the file that calls it (using Ti.include('map.js')). I made a quick and dirty setup to see if it would work:

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';
});

It seems to work better, but there is still a steady decrease in the amount of available memory as I go in and out of the mapview, and the initial memory load makes it as unusable on the devices as the other method (drops memory down to about 3MB).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

心病无药医 2024-11-25 04:56:49

来自有关选项卡/选项卡组的文档...“TabGroup Tab 实例。每个 Tab 实例维护一堆选项卡窗口。选项卡中一次只能看到一个窗口。当窗口关闭时,可以通过用户或通过代码,该窗口将从堆栈中删除,使前一个窗口可见。”

一种猜测是,应用于选项卡时 close() 的行为可能不会像您想象的那样,因为当您在选项卡之间循环时,它似乎会维护选项卡之间的状态。另外,上面的代码示例中可能缺少一些东西,但我实际上没有看到哪里
“win”被定义为一个变量(我假设你有 var win = Ti.UI.currentWindow(); 某处,但你可能需要仔细检查当该函数时它实际上是否被关闭 您还可以考虑

为您的应用程序创建一个对象,并将函数链接到该对象,以免污染全局范围。显示/指南/JavaScript+最佳+实践” rel="nofollow">http://wiki.appcelerator.org/display/guides/JavaScript+Best+Practices

From the docs regarding tabs/tab groups..."A TabGroup Tab instance. Each Tab instance maintains a stack of tab windows. Only one window within in the Tab can be visible at a time. When a window is closed, either by the user or by code, the window is removed from the stack, make the previous window visible."

One guess is that the close() when applied to a tab might not act the way you'd assume it would, since it seems to maintain state between the tabs as you cycle between them. Also, maybe there's something missing in the code example above, but I didn't actually see where
"win" was defined as a variable (i'm assuming you have var win = Ti.UI.currentWindow(); somewhere but you might want to double check that it is actually getting closed when that function is called.

You may also look into creating a single object for your application, and chaining the functions to that object, so as not to pollute the global scope. See: http://wiki.appcelerator.org/display/guides/JavaScript+Best+Practices

倥絔 2024-11-25 04:56:49

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

Is winMap, with its "heavy window" (i.e., pointing to a URL and creating another js context, as opposed to including within the same context) being called each time you go back to the map? I didn't see where it got called from.

淡墨 2024-11-25 04:56:49

您确定正在释放地图视图的内存吗?通过查看代码,我的直觉是它可能是罪魁祸首。

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

are you sure the memory for the mapView is being deallocated? My hunch from looking at the code is that it might be the culprit.

I might suggest using one global mapView object and not keep creating in it map.js

枕头说它不想醒 2024-11-25 04:56:49

我只是想补充一点,如果您将来有更多在 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

我希望这有帮助!

I just wanted to add if you have more projects in the future that are created in Titanium, that there is a recommended way of setting up your application whereby you minimize memory issues.

First, I don't recommend that you use the Ti.include() function. There's a better alternative called require().

I had a number of problems with objects not being garbage collected properly, but these links helped me to write memory-efficient applications:

This is right from Appcelerator:
http://search.vimeo.com/29804284#

This explains the require function and the CommonJS modules:
https://wiki.appcelerator.org/display/guides/CommonJS+Modules+in+Titanium

An example of how to use CommonJS:
https://github.com/appcelerator/Documentation-Examples/blob/master/commonjsExample/Resources/modules/pages/userlist.js

I hope this helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文