Unable to remove Runtime Event Listener

Hi

I am not able to remove a runtime event that listens to notifications. I added the runtime event in the scene:show and remove it in the scene:hide. But anyway, when I change of scene the listener is still there. In order to check if the listener is still there I added a native show alarm that displays when I get a notification.

function scene:show( event ) local sceneGroup = self.view local phase = event.phase local options = event.params if ( phase == "will" ) then -- Called when the scene is still off screen (but is about to come on screen). audio.setVolume(0.8, { channel=2 }) elseif ( phase == "did" ) then composer.removeHidden( ) composer.removeScene(composer.getSceneName( "previous"),false) -- Called when the scene is now on screen. -- Insert code here to make the scene come alive. -- Example: start timers, begin animation, play audio, etc. audio.play( backgroundGameMusic, { channel=2, loops=-1, fadein=1000 }) settings.load() local function notificationListener(event ) if (event.type == "remote" ) then local information = event.custom decodedgame = json.decode(information) if (decodedgame.docId == options.docId) then native.showAlert( "DocID", decodedgame.docId.." = "..options.docId, { "OK" }) end end end Runtime:addEventListener( "system", onSystemEvent ) Runtime:addEventListener( "notification", notificationListener ) end end -- "scene:hide()" function scene:hide( event ) local sceneGroup = self.view local phase = event.phase if ( phase == "will" ) then Runtime:removeEventListener( "notification", notificationListeneridle ) Runtime:removeEventListener( "notification", notificationListener) Runtime:removeEventListener( "system", onSystemEvent ) -- Called when the scene is on screen (but is about to go off screen). -- Insert code here to "pause" the scene. -- Example: stop timers, stop animation, stop audio, etc. elseif ( phase == "did" ) then Runtime:removeEventListener( "system", onSystemEvent ) Runtime:removeEventListener( "notification", notificationListener ) Runtime:removeEventListener( "notification", notificationListeneridle ) -- Called immediately after scene goes off screen. end end

I have read that this is the way to remove runtime listeners, declare the in the show/did and remove them in the  hide. So I dont understand what is wrong with it

Hi there,

This looks like a scoping problem to me.

You have declared your notificationListener function inside your scene:show() function - that means it is local to scene:show and other functions won’t know it exists.  It’s like if you declared a local variable inside of scene:show() - other functions wouldn’t be able to access it either.

Here’s an example:

local function example()        local test = 15    print (test) end example() print (test)

The code above will give you:

15

nil

The second time you try to print the test variable, you get nil, because it is local to function ‘example’.  (Actually, it’d probably crash, because print doesn’t like being passed nil variables, but that’s another issue).

The same is happening with your addEventListener and removeEventListener.  So, inside your scene:show function, you are successfully adding the event listener.  But when you get to scene:hide, Lua is trying to resolve Runtime:removeEventListener(“notification”, notificationListener).  But because the scene:hide doesn’t have access to the function, because it was declared locally in scene:show, what Lua is actually seeing is Runtime:removeEventListener(“notification”, nil), and not doing anything.

To solve the problem, declare your notificationListener outside of your scene:show function in the main body of the code.

Hope that made sense (!?).  If not, you can always Google ‘lua scope’ for more examples.

Hi

I understand and it seems to work :).

Thanks

We just posted a tutorial to the blog this past Tuesday on scope:  https://coronalabs.com/blog/2015/06/16/tutorial-scope-for-beginners/

This is a scoping issue.  The tutorial attempts to explain this and how it impacts composer.  It might be worth the read.

Rob

It actually didn´t work. I have the notificationListener out of the scene:show in the main code and the Runtime Event Listener in the show and the Native Alarm keeps showing in every scene.

If I remove the Runtime Event Listener outside of the scene:show and place it in the main code then it only runs once…which means that when there is a scene overlay the Listener stops working.

Where should the Runtime Event Listener be placed? Since it is about notifications should I remove it in the destroy or in the hide scene?

You declare: 

local function notificationListener(event ) 

inside of scene:show(). Because of that only scene:show() can see that function.  Let’s say your notificationListener has a memory address of 0x283bc2839.  When you do: 

Runtime:addEventListener( "notification", notificationListener )

You’re really doing:

Runtime:addEventListener( "notification", 0x283bc2839 )

In scene:hide() where you call: Runtime:removeEventListener( “notification”, notificationListener),  notificationListener isn’t visible. It’s out of scope.  scene:hide() can’t see local variables in scene:show() so you end up calling:

Runtime:removeEventListener( "notification", nil )

It can’t find a notification listener with a nil address  in the list of listeners, so it fails to remove it.  The solution, detailed in the tutorial I pointed out is to simply move the the notificationListener() function outside of the scene:show() (Don’t nest functions) and this will address your issues.

Rob

It is working. The scope was wrong as you pointed out and then there was a mistake of me when removing the listener.  I was not removing it in the correct place.

Thanks

Hi there,

This looks like a scoping problem to me.

You have declared your notificationListener function inside your scene:show() function - that means it is local to scene:show and other functions won’t know it exists.  It’s like if you declared a local variable inside of scene:show() - other functions wouldn’t be able to access it either.

Here’s an example:

local function example()        local test = 15    print (test) end example() print (test)

The code above will give you:

15

nil

The second time you try to print the test variable, you get nil, because it is local to function ‘example’.  (Actually, it’d probably crash, because print doesn’t like being passed nil variables, but that’s another issue).

The same is happening with your addEventListener and removeEventListener.  So, inside your scene:show function, you are successfully adding the event listener.  But when you get to scene:hide, Lua is trying to resolve Runtime:removeEventListener(“notification”, notificationListener).  But because the scene:hide doesn’t have access to the function, because it was declared locally in scene:show, what Lua is actually seeing is Runtime:removeEventListener(“notification”, nil), and not doing anything.

To solve the problem, declare your notificationListener outside of your scene:show function in the main body of the code.

Hope that made sense (!?).  If not, you can always Google ‘lua scope’ for more examples.

Hi

I understand and it seems to work :).

Thanks

We just posted a tutorial to the blog this past Tuesday on scope:  https://coronalabs.com/blog/2015/06/16/tutorial-scope-for-beginners/

This is a scoping issue.  The tutorial attempts to explain this and how it impacts composer.  It might be worth the read.

Rob

It actually didn´t work. I have the notificationListener out of the scene:show in the main code and the Runtime Event Listener in the show and the Native Alarm keeps showing in every scene.

If I remove the Runtime Event Listener outside of the scene:show and place it in the main code then it only runs once…which means that when there is a scene overlay the Listener stops working.

Where should the Runtime Event Listener be placed? Since it is about notifications should I remove it in the destroy or in the hide scene?

You declare: 

local function notificationListener(event ) 

inside of scene:show(). Because of that only scene:show() can see that function.  Let’s say your notificationListener has a memory address of 0x283bc2839.  When you do: 

Runtime:addEventListener( "notification", notificationListener )

You’re really doing:

Runtime:addEventListener( "notification", 0x283bc2839 )

In scene:hide() where you call: Runtime:removeEventListener( “notification”, notificationListener),  notificationListener isn’t visible. It’s out of scope.  scene:hide() can’t see local variables in scene:show() so you end up calling:

Runtime:removeEventListener( "notification", nil )

It can’t find a notification listener with a nil address  in the list of listeners, so it fails to remove it.  The solution, detailed in the tutorial I pointed out is to simply move the the notificationListener() function outside of the scene:show() (Don’t nest functions) and this will address your issues.

Rob

It is working. The scope was wrong as you pointed out and then there was a mistake of me when removing the listener.  I was not removing it in the correct place.

Thanks