display.loadRemoteImage seems to cache files, even though I delete the file first...

Problem: Image doesn’t update in a view. Actually, two different images used in two different views.

Two different image URLs are loaded to the same filename in system.TemporaryFiles. The file should update, but it doesn’t. So I added a line to delete the file with os.remove just before the call to loadRemoteImage, and still I get the same image when newImageRecting it in the view.

What have I tried:

  • made sure all view variables are local 

  • checked so the image isn’t staying and covering the real image in the view somehow

  • renamed the destination filename for the second file. then it works. but I can’t really have a filename generator for each image that I load, I’d fill up the phone’s storage.

Any ideas? :slight_smile:

Edit: os.remove didn’t delete the file because it is being kept open, even though the previously loaded image has resulted in an object. Unfortunately I’m using a mobile web page menu which means I can’t removeSelf() the view, which might close the file. 

The file being open also affects loadRemoteImage being able to overwrite the file. But I get no error message in its listener, it just pretends it loaded fine and gives me a valid image object in target – just the previously loaded image.

Still working out how to do this without display.remove()ing the old image. Cyclically numbered file names, perhaps.

Answer: cyclically numbered file names worked around the issue.

Welcome to iOS 10’s super agressive caching.

The work around is to use a unique URL for each image. Let’s say you always need to download:

https://somesite.com/screenshot.png

The OS will say Oh I have that already, no need to downloaded it and serve up the cached file even though the requested file is newer than the cached one.  The solution is to use an old cache busting trick:

https://somesite.com/screenshot.png?cb=” … tostring(os.time())

The query parameters will be ignored and the file downloaded, but iOS will think you’re requesting a different file. Instead of os.time() you could use a number that just increments and saved between runs.

By the way “cb” stands for cache busting, but the text isn’t that important, you just as well could use ?xyzzy= since nothing will actually look at the query parameter on the server.

Rob

Thx Rob but that’s not it I think, or rather we’re counting on that. The URLs were cachebusted, and they pointed to different image files.

This happened in the Corona Simulator as well.

I think the file is still open as per this thread, but mostly because I had 3 views, and the one where self.view:hide() “did” freed the view (and presumably allowed the file to close), that one worked.

A caching scheme that caches a second completely different source file just because the destination filename is the same would be dysfunctional, not aggressive :slight_smile:

I think there’s some handle connected to e.target in the download listener that is not closed, even if you dispose of e.target with display.remove() or :removeSelf(). There might be a difference to network.download().

Just a thought…

Problem: you load image1.png and then you want to refresh image1.png from a different URL.  If you do not kill ALL references to this object in Corona then then next time you try and access image1.png (even if you are loading from file system) Corona will simply return you the image it already has in memory.

Perhaps this is your problem?

Well, the object is not refreshed. A new object is created in the listener. The main problem is that the event that removes the scene’s view or display objects, hide()'s “did” phase in the previous scene, doesn’t trigger in time from a gotoScene to another view. I.e. not until the previous scene “did” move offscreen. The alternative is to blank the screen every time you select an item in the app’s menu.

You could say, “well, copy the filename from the file server”. That’s the best caching policy if you can ensure unique, new image file names every time they’re changed.

This is a technical observation of something that lies between a phone’s local storage and the Corona code. It was just a stumper for someone conversant in matters.

Let me rephrase.  Assume you download image1.png which is a black car.  You load that into Corona.  When you next download image1.png (which is now a red car) and you tell Corona to load image1.png you will still get a black car.

just to second what SGS is saying… Corona’s internal texture caching mechanism is “indexed” by filename.  So when you do:

myImage = display.newImage("car.png", ..

Corona loads that texture and “identifies” it by it’s filename “car.png”.  As far as Corona knows, the contents of “car.png” is assumed to be “static”, so it can use that ‘fact’ to implement caching.  If you later need to load that same image again (say it’s a game, and you have many instances of the same car) Corona will say to itself “oh, I already have ‘car.png’ loaded, I’ll just return the in-memory texture and save a disk load”. (AND, critically though just an aside for this discussion here, also saving the additional texture memory that would have been required if truly loaded more than once)

That is *exactly* what Corona’s texture-caching mechanism is intended to do.

The way to “defeat” it, and force a reload of a texture, is to wipe all references to that image so that it is freed from memory.  (even a SINGLE remaining reference will keep the texture in memory, keep the cache valid, and prevent a reload)

display.remove(myImage) -- remove from display myImage = nil -- release references myImage = display.newImage("car.png",.. -- must reload

Answer: cyclically numbered file names worked around the issue.

Welcome to iOS 10’s super agressive caching.

The work around is to use a unique URL for each image. Let’s say you always need to download:

https://somesite.com/screenshot.png

The OS will say Oh I have that already, no need to downloaded it and serve up the cached file even though the requested file is newer than the cached one.  The solution is to use an old cache busting trick:

https://somesite.com/screenshot.png?cb=” … tostring(os.time())

The query parameters will be ignored and the file downloaded, but iOS will think you’re requesting a different file. Instead of os.time() you could use a number that just increments and saved between runs.

By the way “cb” stands for cache busting, but the text isn’t that important, you just as well could use ?xyzzy= since nothing will actually look at the query parameter on the server.

Rob

Thx Rob but that’s not it I think, or rather we’re counting on that. The URLs were cachebusted, and they pointed to different image files.

This happened in the Corona Simulator as well.

I think the file is still open as per this thread, but mostly because I had 3 views, and the one where self.view:hide() “did” freed the view (and presumably allowed the file to close), that one worked.

A caching scheme that caches a second completely different source file just because the destination filename is the same would be dysfunctional, not aggressive :slight_smile:

I think there’s some handle connected to e.target in the download listener that is not closed, even if you dispose of e.target with display.remove() or :removeSelf(). There might be a difference to network.download().

Just a thought…

Problem: you load image1.png and then you want to refresh image1.png from a different URL.  If you do not kill ALL references to this object in Corona then then next time you try and access image1.png (even if you are loading from file system) Corona will simply return you the image it already has in memory.

Perhaps this is your problem?

Well, the object is not refreshed. A new object is created in the listener. The main problem is that the event that removes the scene’s view or display objects, hide()'s “did” phase in the previous scene, doesn’t trigger in time from a gotoScene to another view. I.e. not until the previous scene “did” move offscreen. The alternative is to blank the screen every time you select an item in the app’s menu.

You could say, “well, copy the filename from the file server”. That’s the best caching policy if you can ensure unique, new image file names every time they’re changed.

This is a technical observation of something that lies between a phone’s local storage and the Corona code. It was just a stumper for someone conversant in matters.

Let me rephrase.  Assume you download image1.png which is a black car.  You load that into Corona.  When you next download image1.png (which is now a red car) and you tell Corona to load image1.png you will still get a black car.

just to second what SGS is saying… Corona’s internal texture caching mechanism is “indexed” by filename.  So when you do:

myImage = display.newImage("car.png", ..

Corona loads that texture and “identifies” it by it’s filename “car.png”.  As far as Corona knows, the contents of “car.png” is assumed to be “static”, so it can use that ‘fact’ to implement caching.  If you later need to load that same image again (say it’s a game, and you have many instances of the same car) Corona will say to itself “oh, I already have ‘car.png’ loaded, I’ll just return the in-memory texture and save a disk load”. (AND, critically though just an aside for this discussion here, also saving the additional texture memory that would have been required if truly loaded more than once)

That is *exactly* what Corona’s texture-caching mechanism is intended to do.

The way to “defeat” it, and force a reload of a texture, is to wipe all references to that image so that it is freed from memory.  (even a SINGLE remaining reference will keep the texture in memory, keep the cache valid, and prevent a reload)

display.remove(myImage) -- remove from display myImage = nil -- release references myImage = display.newImage("car.png",.. -- must reload