我有一个应用程序是使用 Appcelerator Titanium(适用于 iOS,Ti 和 Apple 的最新 SDK)构建的,其中一部分严重依赖于 map 。我遇到的问题是,当我关闭包含 MapView 的窗口时,内存似乎没有释放。因此,在菜单屏幕和 map 之间来回切换会降低 iPhone 的速度,直到它最终完全停止响应(加载 3-5 个 map )。
我使用 Titanium 的 Ti.Platform.availableMemory
调用来查看进入带有 map 的窗口时以及 map 关闭后的内存。结果是每一次连续进入/退出都呈稳定下降趋势,沿着:
25(map.js 的初始加载)
20(注释后)
20(在 win.close()
之后)
19(第二次加载map.js)
18(注释)
19(离开)
18(输入)
16(注释)
15(离开)
在模拟器中,当窗口关闭时它可能会上升一点,但即使它显示出稳定的下降趋势。
这是 map 的代码,位于它自己的“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();
});
这是加载 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;
});
我制作了一个全局 HTTPClient 并重用它,就像其他一些问答答案(在 SO 和 Appcelerator 的网站上)所建议的那样,这似乎有所帮助(每次 map 加载不会耗尽太多内存),我还尝试手动将变量(尤其是较大的变量)设置为 null(这可能有效也可能无效),但仍然存在一些问题。我还尝试在事件监听器中为打开窗口的按钮创建 map 窗口,但这似乎根本没有任何效果。
我还运行了 Instruments 看看它能找到什么,但没有发现任何值得注意的东西(我什至把它展示给我的同事,他全职做移动开发,他说他没有发现任何异常可以看到)。
我已经查看这段代码几个小时了,这不是我的全部代码,所以我完全有可能遗漏了一些明显的东西,但是我的代码中是否有原因导致内存不可用应该释放吗?还有什么我可以做的来释放更多的内存吗?我现在只针对 iOS 进行开发,因此可以接受特定于 iOS 的解决方案。
编辑 - 我现在还尝试将 map 部分包含到调用它的文件中(使用 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)。
最佳答案
来自关于选项卡/选项卡组的文档...“一个 TabGroup Tab 实例。每个 Tab 实例维护一堆选项卡窗口。一次只能看到选项卡中的一个窗口。当窗口关闭时,由用户或代码将窗口从堆栈中移除,使前一个窗口可见。”
一种猜测是 close()
在应用于选项卡时可能不会按照您假设的方式运行,因为它似乎在您在选项卡之间循环时保持选项卡之间的状态。另外,也许上面的代码示例中缺少一些东西,但我实际上并没有看到在哪里
“win”被定义为一个变量(我假设你在某处有 var win = Ti.UI.currentWindow();
但你可能想仔细检查它是否真的在该函数时关闭被称为。
您还可以考虑为您的应用程序创建单个对象,并将函数链接到该对象,以免污染全局范围。请参阅:http://wiki.appcelerator.org/display/guides/JavaScript+Best+Practices
关于ios - Titanium MapView 不释放内存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6538555/