我有一个使用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)。
答案 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
我希望这有帮助!