Uploaded image for project: 'Titanium SDK/CLI'
  1. Titanium SDK/CLI
  2. TIMOB-13747

Android: Memory leak when ListView template contains an event listener

    Details

    • Type: Bug
    • Status: Closed
    • Priority: High
    • Resolution: Invalid
    • Affects Version/s: None
    • Fix Version/s: None
    • Component/s: Android
    • Labels:
      None
    • Sprint:
      2014 Sprint 09 Tooling, 2014 Sprint 11 SDK

      Description

      Problem

      Memory leak when ListView template contains an event listener.

      Titanium SDK 3.2.0 (HEAD)
      Android 4.2.1
      Nexus S

      Only tested this on Android. Maybe it should be checked if this leak also occurs on iOS.

      Test case

      The code for the test case is taken from the ListItem example in the documentation. It has been extended, so that the window with the list view

      • can be opened and closed as to expose the memory leak
      • is a heavyweight window

      app.js

      var rootWin = Ti.UI.createWindow({});
      var button = Ti.UI.createButton({
      	title: 'Open'
      });
       
      button.addEventListener('click', function(evt){
      	
      	var win = Ti.UI.createWindow({backgroundColor: 'white', modal: false });
       
      	var plainTemplate = {
      	    childTemplates: [
      	        {                            // Image justified left
      	            type: 'Ti.UI.ImageView', // Use an image view for the image
      	            bindId: 'pic',           // Maps to a custom pic property of the item data
      	            properties: {            // Sets the image view properties
      	                width: '50dp', height: '50dp', left: 0
      	            }
      	        },
      	        {                            // Title
      	            type: 'Ti.UI.Label',     // Use a label for the title
      	            bindId: 'title',         // Maps to a custom title property of the item data
      	            properties: {            // Sets the label properties
      	                color: 'black',
      	                font: { fontFamily:'Arial', fontSize: '20dp', fontWeight:'bold' },
      	                left: '60dp', top: 0,
      	            },
      	        },
      	        {                            // Subtitle
      	            type: 'Ti.UI.Label',     // Use a label for the subtitle
      	            bindId: 'subtitle',      // Maps to a custom subtitle property of the item data
      	            properties: {            // Sets the label properties
      	                color: 'gray',
      	                font: { fontFamily:'Arial', fontSize: '14dp' },
      	                left: '60dp', top: '25dp',
      	            }
      	        }
      	    ],
      	    // Binds a callback to the click event, which catches events bubbled by the view subcomponents.
      	    events: {click: toggleCheck }
      	};
       
      	// The following API calls are equivalent to using jQuery.extend(true, {}, oldObject)
      	// Copy the plainTemplate
      	var redTemplate = JSON.parse(JSON.stringify(plainTemplate));
      	// Change the text color to red
      	redTemplate.childTemplates[1].properties.color = 'red';
      	redTemplate.childTemplates[2].properties.color = 'red';
      	// Rebind the click event callback
      	redTemplate.events.click = toggleCheck;
       
      	var listView = Ti.UI.createListView({
      	    // Maps plainTemplate to 'uncheck' and redTemplate to 'check' 
      	    templates: { 'uncheck': plainTemplate, 'check': redTemplate },
      	    // Use 'uncheck', that is, the plainTemplate created earlier for all items
      	    // Can be overridden by the item's template property
      	    defaultItemTemplate: 'uncheck'
      	});
       
      	var tasks = [
      	    {id: 'trash', name: 'Take Out the Trash', person: 'Yakko', icon: 'trash.png'},
      	    {id: 'dishes', name: 'Do the Dishes', person: 'Wakko', icon: 'dishes.png'},
      	    {id: 'doggie', name: 'Walk the Dog', person: 'Dot', icon: 'doggie.png'}
      	];
       
      	var data = [];
      	for (var i = 0; i < tasks.length; i++) {
      	    data.push({
      	        // Maps to the title component in the template
      	        // Sets the text property of the Label component
      	        title : { text: tasks[i].name },
      	        // Maps to the subtitle component in the template
      	        // Sets the text property of the Label component
      	        subtitle : { text : tasks[i].person },
      	        // Maps to the pic component in the template
      	        // Sets the image property of the ImageView component
      	        pic : { image : tasks[i].icon },
      	        // Sets the regular list data properties
      	        properties : {
      	            itemId: tasks[i].id,
      	            accessoryType: Ti.UI.LIST_ACCESSORY_TYPE_NONE,
      	        }
      	    });
      	}
       
      	var section = Ti.UI.createListSection();
      	section.setItems(data);
      	listView.sections = [section];
       
      	// Modified version of the `itemclick` event listener
      	// Changes the item template rather than the list item's color property
      	function toggleCheck (e) {
      	    var item = section.getItemAt(e.itemIndex);
      	    if (item.properties.accessoryType == Ti.UI.LIST_ACCESSORY_TYPE_NONE) {
      	        item.properties.accessoryType = Ti.UI.LIST_ACCESSORY_TYPE_CHECKMARK;
      	        item.template = 'check';
      	    }
      	    else {
      	        item.properties.accessoryType = Ti.UI.LIST_ACCESSORY_TYPE_NONE;
      	        item.template = 'uncheck';
      	    }
      	    section.updateItemAt(e.itemIndex, item);
      	} 
       
      	win.add(listView);
      	win.open();
      	
      	
      });
       
      rootWin.add(button);
      rootWin.open();
      

      Steps to reproduce:
      1. Start the application
      2. Create a heap dump in DDMS
      3. Tap the 'open' button
      4. Tap the back button
      5. Cause GC
      6. Create a second heap dump in DDMS
      7. Do a comparison of the two heap dumps with Eclipse Memory Analyzer. The second heap dump has multiple new and living objects compared to the first heap dump, e.g. TiBaseWindowProxy, ListViewProxy, etc. This does not happen if the event listener is removed in the template.

        Attachments

          Activity

            People

            • Assignee:
              Unassigned
              Reporter:
              philet Philippe Wueger
            • Watchers:
              0 Start watching this issue

              Dates

              • Created:
                Updated:
                Resolved:

                Backbone Issue Sync

                • Titanium SDK/CLI <> Titanium Mobile
                  Synced with:
                  TIMOB-16397
                  Sync status:
                  ERROR
                  Last received:
                  Last sent:

                  Git Integration