How to prevent loading image by loadRemoteImage function if scene has changed

From time to time in my application an image loaded by loadRemoteImage is shown on top of everything else and travels from scene to scene though it supposed to be inserted into another object as paint, etc.
I will explain what steps I did to prevent such a situation, but may be it is not enough, also I need advice from the developers of the framework as they know better how it works.

To prevent such a situation with an image in the listener sub (first) and in a few other places I put a scene name check…

local tmp_str_a = composer.getSceneName("current")
if tmp_str_a ~= "scenes.MYSCENE" then
	display.remove(event.target)
-- or display.remove(obj) -- depending on the place in the code
	return
end

Second, In “ended” phase I pcall a function that inserts a freshly-downloaded image into a circle. Here is a code that creates a circle, adds an image and shows the circle in a 1/4sec. (Scene-name checks are presented there, too)

local a_circle = display.newCircle(event.target.x, event.target.y, user_pic_WH_int/2)
		local paint = {
			type = "image",
			filename = event.response.filename,
			baseDir = system.ApplicationSupportDirectory
			}
			-- Fill the circle
		a_circle.fill = paint
		local tmp_bool_a = true
		tmp_bool_a = pcall(sv_insert_sub, a_circle)
		if tmp_bool_a == false then
			display.remove(a_circle)
			display.remove(event.target)
			return
		end
		event.target.alpha = 0
		transition.to(
			a_circle,
			{time = loading_speed_int,
			alpha = 1.0}
		)
local function sv_insert_sub(obj)
	SV:insert(obj) -- scrollView
end

So, what am I missing here? What is the behavior of the listener-function if a scene got changed before the image was loaded. Should I block a user to change a scene if “began” phase is in progress? But what if it takes too long?

Or simply explain how to remove that internal image that I cannot get rid of :slight_smile:

Are you not cancelling the process when transitioning to next scene?

What line does it in the sample code?
I think I do cancel:

if key_str == "HTTP/1.1 404 Not Found" then
	display.remove(event.target)
	return
end
if key_str == "404" then
	display.remove(event.target)
	return
elseif key_str == "500" then
	display.remove(event.target)
	return
end
if (event.isError) then
	print("Network error - download failed: ", event.response)
	display.remove(event.target)
	return
end

I thought the sample code would have been more straightforward, but I guess not. :slightly_smiling_face:

Those have valid scenarios, but I think for you it’s probably best to actually cancel the network request. You’ll need to incorporate it in your code.

I am not using network.request, I use display.loadRemoteImage. How to cancel it… is a question even raised here:

They say not to use display.loadRemoteImage at all! Do I have to lodge a feature request? :slight_smile:

Well, the comment in the cancel-remote-load-before-completion pretty much explains it:

--The 'display.loadRemoteImage()' API call is a convenience method around the 'network.request()' API.
--The code below is the implementation of 'display.loadRemoteImage()'. If you need to cancel your call,
--feel free to use the code below and modify it to your needs.

In other words, you are indeed using network request (indirectly), there’s no other way to get an image from a remote source. :slightly_smiling_face:

The sample code for cancelling loadRemoteImage, like RoamingGamer commented in that post, is not complete, but it does give you enough information to create your own display.loadRemoteImage function/workflow.

Since there’s no simple “cancel the function mechanism”, it needs to be implemented with some creativity.
Looking at the cancel network request code, you can see that all you need is a handle, thus the following changes from the sample code is all you would need:

local requestId -- somewhere outside your custom display.loadRemoteImage()
----------------------------------------------------

if ( params ) then
   requestId = network.download( url, method, options, params, destFilename, baseDir )
else
   requestId = network.download( url, method, options, destFilename, baseDir )
end
-------------------------------------------

 network.cancel( requestId ) -- somewhere in your scene to cancel the request.

The above is one way to go about it.

If you don’t want do deal with canceling the network request, then you still need to have a custom display.loadRemoteImage function, or break down its workflow to suit your needs, with a variable that you can use to go ahead and continue using the remote image, or bail out.

The reason I would chose to cancel the network request is simply because I wouldn’t want a network request lingering in the background when I already left that scene, unless the request needs to be completed regardless. :grinning_face_with_smiling_eyes:

My code works like this: once loaded the images go as paint to different objects and then deleted. The problem is that they sometimes are not deleted and stay on the screen … and I cannot get why. I thought that it happens because the scene is changed, before the image is loaded, but it is not so… I do not understand why it happens… and I cannot really catch this situation as it happens 1 of 20-30 loads…
So, I will go a simple way, instead of using custom solutions, all my images will be loaded onto coordinates beyond visibility :slight_smile: Done :slight_smile:

I understand you don’t want to complicate things, no one does :grin:, but if I understood correctly, you’re using loadRemoteImage to download an image only to be used for obj.fill, and if so then it’s probably not the way to go; reason being loadRemoteImage will download and display them on the screen, which I suspect is what’s being left behind.

If you’re ever up for some code changes then you should just download the files via network request, and use them as needed, you won’t have to worry about anything else. Otherwise, your current solution will create a memory leak with images that can no longer be removed. You can only sweep the dust under the rug for so long. :slightly_smiling_face:

I’m not sure if this helps:

But I always use Scene Name to exit any function that can cause trouble when changing scenes
I mean everything!
so i always have this block of code at the top of any function or listener that is expected to keep working after changing scenes:

if sceneName ~= "SceneNameBeingExited" then
      return true
end

Where sceneName is a Global variable I use to name my scenes … I never used

composer.getSceneName("current")

and I always use sceneName variable at the top of each scene code like this for example:

sceneName = "login"

and this works for me perfectly because almost all of my apps deal with web services that download or upload data and images

Scene change is not the reason of the problem I encountered with, there must be something else…

I chose loadRemoteImage for a specific reason… When image is loaded it suppose to go on a certain place defined by x & y, which paremeters of loadRemoteImage function… but not of network.download. Gotta think of a work-around…

you can use network.download and add any parameters to listener

you can add as many parameters as you wish

local function remoteImageListenerUser( self, event )
    if sceneName~="NameOfCurrentScene" or scrollView==nil then--if your image was in a scroll view
      return true
    end
    local listener = self.listener

    if ( not event.isError and event.phase == "ended" ) then
        local filePath0 = system.pathForFile( self.filename, system.TemporaryDirectory )
        local myFile0 = io.open( filePath0, "r" )
        if myFile0 then
          io.close( myFile0 )
          local target = display.newImage(self.filename, self.baseDir)
          target.x=self.x
          target.y=self.y
          if scrollView~=nil and target~=nil then--if your image was in a scroll view
              scrollView:insert(target)
          end
       end
end
local function getUserImage(theX,theY)
  local user_fileName="YourImageName.extension"
  local params
  local options = {
          x=theX,
          y=theY,
          filename=user_fileName, 
          baseDir=system.TemporaryDirectory,
          networkRequest=remoteImageListenerUser,
          listener=listener
      }
  local currPath="URL_To_Your_Image"
  network.download( currPath, "GET", options, params,user_fileName, system.TemporaryDirectory )
end

getUserImage(200,300)
1 Like

Thank you. For me it was not clear that custom user params could be passed to network.download function. I still cannot achieve it…

Can you please brush up the code a litttle bit, what is the difference between options and params? How does this “self” work?

Frankly speaking I don’t really know the difference :grin:

I never bothered myself searching for it, when it first worked with me, I kept doing copy paste for this code block in every app I make where this code is needed

Most of the times I load objects from a URL and put them in a scroll view, and those objects have IDs, Names, Locations etc.

And when the objects are shown via the listener I start associating tap events and any other properties or methods based on the values they hold

As for self, I’m not sure if I was the best person to explain it, although I understand it very well, but in general Solar2D allows you to pass multiple parameters via functions. and when those functions are triggered by an object like a downloaded image, or a tapped or touched object, mostly you have event and self as parameters to that particular function

One of the biggest differences between the two is that self passes the object with its values and event passes the objects with its values and events

So when using self you recognize the object being passed, but you cannot know which event was triggered like tap, touch, began, ended

Look at the below example for self for rotating a rectangle … of course this is not the best way to do it but this is a good example of using self:

local function rotateBack(self)
       if self.rotation>0 then
                 transition.to(self,{time=500,rotation=0,onComplete=rotateBack})
       else
                 transition.to(self,{time=500,rotation=100,onComplete=rotateBack})
        end
end
local rect1 = display.newRect(100,100,100,100)
rect1:setFillColor(1,0,0)
transition.to(rect1,{time=500,rotation=100,onComplete=rotateBack})

I got another trick here. X&Y can be passed in header to the server and the server can send it back in the header as well :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.