I am currently working on a very large HTML5 project on the iPad, and so far I’ve really enjoyed the experience. I plan on doing a longer blogpost containing all the things that I have learned (and maybe some code to share
) but for now I would like to share with everyone a BIG problem I have encountered and the solution that I have found.
The application I am working on contains a large amount of graphical elements, at one point displaying upwards of 24 1024×768 images that can be manipulated via Javascript touch events. At first this didn’t seem possible, but after discovering and using the webkit-transform properties (which enable hardware acceleration) it has been pretty smooth sailing. Recently the application has grown in size to the point that I have been hitting the upper limits of the iPads available memory for Mobile Safari. This results in the not so graceful error of a hard crash by the browser.
If you want to learn more about the issue and my research, read below, if not just skip to the solution section and ignore the rest.
Background
Coming from a background of C++/Objective-C development and having done some native apps in the past, I knew that this usually results from an application running out of memory. Safari may be a browser, but when it comes down to it there is little difference between Safari and any other application in the app store. Normally I would be very conscious of my memory usage and make sure to free any assets that weren’t currently being used.
Memory management of this type is not possible in Safari. The browser caches everything that is loaded into memory and attempts to maintain that cache while the app is open. It handles this in a very smart way (only loading things as they enter the display space) so when the app loads as long as I have all divs that aren’t being displayed set to display:none the browser ignores any assets. As the user starts to interact with the application, they move through the interface and reveal images. These images are loaded into memory, and remain there. This basically results in a pile of trash (garbage
) that begins to pile up. In the case of my application, once a user has gotten through the majority of the app I am pushing upwards of 50 1024×768 images loaded in memory! If a user goes back into a section there is no load time, the images are already there. This is great for a normal website where the user leaves pages constantly and the local memory is freed. A web app on the other hand is essentially 1 web page. In the case of my application this one page consists of thousands of lines of code and can potentially load up to 5-6 mb of data. Worse, it isn’t even done yet and will probably bloat more very soon.
After a good amount of research, I found out that the memory limit for an iPad web app is about 5mb. After discovering this earlier on I applied very aggressive compression to all images and managed to get the app stable again. Now that I have started adding more assets, this will no longer do the trick.
Solution
There is no way in javascript to “free” an image per-say, regardless of whether it is in the html (lazy-loaded) or creating dynamically through the DOM using javascript. You CAN however, trigger events that will cause the browser to unload an image from memory. The easiest (and I think best) way to do this is to replace the SRC of every image you want to hide with 1 tiny image. In my case I am using a 1px solid white png that is miniscule in size. This causes the browser to reload the image, at which time it boots out the cached, “real” image out of memory and replaces it with this tiny image. If you are hiding and showing your divs (which you should be if you have something this big) the only repercussion that this will have is that you will perpetually see your images loading every time you show a div. In most cases this probably isn’t an issue, but if it is you can selectively choose to not use this method in certain areas. The main point is that you have the CHOICE to do this which is great.
One immediate way that I can see this being extremely useful is for any application that loads in an endless amount of images (infinite carousel or something), for example a movie library browsing app. Instead of deleting old images (which keeps them cached) and adding new ones, simply changing the src tag will ensure that you never have more images loaded than are being displayed.
This may be a big “no shit” thing to some javascript devs out there, but I had a tough time finding any real documentation on it, so hopefully it will be useful to others trying to do mobile web dev.
Here is a very simple class I wrote that shows what I am talking about. You create a new instance and feed it the element you want to show/hide and the URL of a placeholder image, and it does the simple work for you.
/*Vtron Image Manager*/
/*Copyright 2010 Stephen Varga, Vargatron*/
function VtronImageManager(element,placeholderURL) {
this.element = element;
this.placeholder = placeholderURL;
this.images = this.element.getElementsByTagName('img');
this.imageURLs = this.getImageURLs();
}
VtronImageManager.prototype = {
show: function() {
for(var i=0; i < this.images.length; i++) {
this.images[i].src = this.imageURLs[i];
}
},
hide: function() {
for(var i=0; i < this.images.length; i++) {
this.images[i].src = this.placeholderURL;
}
},
getImageURLs: function() {
var imageURLs = new Array();
for(var i=0;i < this.images.length;i++) {
imageURLs.push(this.images[i].src);
}
return imageURLs;
}
}