Titanium MapView не освобождает память - PullRequest
1 голос
/ 30 июня 2011

У меня есть приложение, созданное с использованием Appcelerator Titanium (для iOS, последних SDK от Ti и Apple), и часть его в значительной степени опирается на карты.У меня проблема в том, что память не высвобождается, когда я закрываю окно, содержащее MapView.В результате переход с экрана меню на карту и обратно замедляет работу iPhone до тех пор, пока он не перестанет отвечать вообще (загрузка 3-5 карт).

Я использовал вызов Ti.Platform.availableMemory от Titanium, чтобы увидеть памятьпри входе в окно с картой и после закрытия карты.Результатом является устойчивый нисходящий тренд с каждым последующим входом / выходом вдоль линий:

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), что, похоже, помогло (не так много памятиопустошенный при каждой загрузке карты), я также пытался вручную установить переменные (особенно большие) в ноль (что может или не может быть эффективным), но что-то все еще держится.Я также попытался создать окно карты внутри прослушивателя событий для кнопок, открывающих окно, но это, похоже, не дало никакого эффекта.

Я также запустил Инструменты, чтобы посмотреть, что он может найти, ине нашел ничего примечательного (я даже показал это своему коллеге, который занимается мобильной разработкой полный рабочий день, и он сказал, что не было ничего необычного, что он мог видеть).

Я искалв этом коде уже несколько часов, и это не весь мой код, так что вполне возможно, что я упускаю что-то очевидное, но есть ли причина в моем коде, почему память не освобождается должным образом?Могу ли я сделать что-то еще, чтобы освободить больше памяти?Сейчас я занимаюсь только разработкой для 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, и начальная загрузка памяти делает его таким же непригодным для использования на устройствах, как и другой метод (объем памяти уменьшается примерно до 3 МБ).

Ответы [ 4 ]

1 голос
/ 14 июля 2011

Из документов, касающихся вкладок / групп вкладок ... "Экземпляр вкладки TabGroup. Каждый экземпляр вкладки поддерживает стек окон вкладок. Только одно окно внутри вкладки может быть видно одновременно. Когда окно закрыто,либо пользователем, либо кодом, окно удаляется из стека, делая предыдущее окно видимым. "

Можно предположить, что close() при применении к вкладке может не работать так, как выПредположим, что так и будет, поскольку кажется, что он поддерживает состояние между вкладками, когда вы переключаетесь между ними.Кроме того, возможно, в приведенном выше примере кода чего-то не хватает, но я на самом деле не видел, где «win» был определен как переменная (я предполагаю, что у вас где-то есть var win = Ti.UI.currentWindow();, но вы можете проверить, что это на самом делезакрытие при вызове этой функции.

Вы также можете посмотреть, как создать отдельный объект для вашего приложения и связать функции с этим объектом, чтобы не загрязнять глобальную область видимости. См .: http://wiki.appcelerator.org/display/guides/JavaScript+Best+Practices

0 голосов
/ 20 декабря 2011

Я просто хотел добавить, что если в будущем у вас будет больше проектов, созданных в 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

Надеюсь, это поможет!

0 голосов
/ 07 июля 2011

вы уверены, что память для mapView освобождается? Я догадываюсь, что этот код может быть виновником.

Я мог бы предложить использовать один глобальный объект mapView и не продолжать создавать в нем map.js

0 голосов
/ 07 июля 2011

WinMap с его «тяжелым окном» (т. Е. Указывающим на URL-адрес и создающим другой JS-контекст, в отличие от включения в тот же контекст) вызывается каждый раз, когда вы возвращаетесь к карте?Я не видел, откуда он звонил.

...