【发布时间】:2011-06-30 17:15:13
【问题描述】:
我有一个使用 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';
});
它似乎工作得更好,但是当我进出 mapview 时,可用内存量仍然在稳步减少,并且初始内存负载使其在设备上与其他方法一样无法使用(drops内存减少到大约 3MB)。
【问题讨论】:
-
developer.appcelerator.com/question/116867/… 刚刚遇到这个。也许它可以提供帮助。
-
@tgriesser - 我很久以前就发现了。它实际上是影响上述代码以及我如何处理地图的主要解决方案之一。
-
您是否尝试过将 map.js 中的所有内容封装在一个自调用匿名函数中?
-
@tgriesser - 我没有尝试过,但我目前不再参与该项目。
标签: ios memory-management mobile titanium