Skip to content

love.quit, love.focus and love.visible callbacks not being triggered on Android #2171

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
Grench6 opened this issue Mar 25, 2025 · 5 comments
Labels
Android library dependency Related to a library used by LÖVE

Comments

@Grench6
Copy link

Grench6 commented Mar 25, 2025

Consider the following code which tracks the number of times the callbacks quit, focus, and visible gets called (using a file for persistence):

-- main.lua

function love.load()
    loadData()
end

function love.draw()
    love.graphics.print('quit: '..data.quit..', visible: '..data.visible..', focus: '..data.focus)
end

function love.quit()
    data.quit = data.quit + 1
    saveData()
end

function love.visible(visible)
    if not visible then
        data.visible = data.visible + 1
        saveData()
    end
end

function love.focus(focused)
    if not focused then
        data.focus = data.focus + 1
        saveData()
    end
end

-- Data persistence functions:

function loadData()
    if love.filesystem.getInfo('data.lua') then
        local content = love.filesystem.read('data.lua')
        local chunk = load(content)
        data = chunk()
    else
        data = {quit=0, visible=0, focus=0}
    end
end

function saveData()
    local content = string.format('return {quit=%q, visible=%q, focus=%q}', data.quit, data.visible, data.focus)
    love.filesystem.write('data.lua', content)
end

Now if you generate an embedded APK with this and experiment with it a little, you will see that the callbacks almost never get called (but some of them do get called a few times, like 10% of the time). I am not sure why this inconsistent behavior happens and it doesn't work 90% of the time.

I am trying to save a little bit of game state when the user closes the app, and while I have implemented some sort of auto-save feature every minute or so, I would like to reliably (most of the time) be able to execute a little bit of code to save the state when the user quits the app. Initially I tried with quit, but after seeing it didn't work, I tried with focus and visible too, to find a workaround.

I don't know if there is currently a way to get callbacks for important activity lifecycle events, or at least the most important/useful one... an event when the user exits the app!. It seems to me that SDL for android does support this.

Are there currently any workarounds?

Software version

Desktop:

  • Windows 11
  • Love2D/love: LOVE 11.5 (Mysterious Mysteries)

Mobile:

  • Android 10
  • Love2D/love-android: LOVE 11.5 Hotfix 1 (Mysterious Mysteries)
@Grench6
Copy link
Author

Grench6 commented Mar 27, 2025

Small update on what I've tried

In order to try to have a reliable event on android app pause, I added two lines to src/modules/event/sdl/Event.cpp to detect SDL_EVENT_WILL_ENTER_BACKGROUND and SDL_EVENT_DID_ENTER_BACKGROUND:

// Event.cpp line 496
case SDL_EVENT_QUIT:
case SDL_EVENT_TERMINATING:
case SDL_EVENT_WILL_ENTER_BACKGROUND:
case SDL_EVENT_DID_ENTER_BACKGROUND:
  msg = new Message("quit");
  break;
// ...

This is because they mention in the docs that these events are for when "The application is about to enter the background and may not get CPU for some time. Called on Android in onPause()."

However, this doesn't seem to solve the problem or be any better (in fact, quit never gets called when the app enters background).

Edit: Fixed a typo in the snippet.

@MikuAuahDark
Copy link
Contributor

MikuAuahDark commented Mar 27, 2025

Can you try to add this code in conf.lua then report back if it still same issue?

local ffi = require("ffi")
ffi.cdef("int SDL_SetHint(const char* name, const char* value);")
ffi.C.SDL_SetHint("SDL_ANDROID_BLOCK_ON_PAUSE", "0")

I need to rule out something before invstigating further.

@Grench6
Copy link
Author

Grench6 commented Mar 27, 2025

Hello @MikuAuahDark, thank you for your response.

I tested your changes with the official binary and now love.focus works and gets called 100% of the time!

However, love.quit still doesn't work consistently, and while love.focus is a good-enough solution for me (considering what I was trying to achieve), it would undeniably be better for love.quit to work too.

Do we need any additional changes besides setting the hint SDL_ANDROID_BLOCK_ON_PAUSE to false by default? And will this negatively affect battery consumption? I ask because I saw this in the docs:

If SDL_HINT_ANDROID_BLOCK_ON_PAUSE hint is set (the default), the event loop will block itself when the app is paused (ie, when the user returns to the main Android dashboard). Blocking is better in terms of battery use, and it allows your app to spring back to life instantaneously after resume (versus polling for a resume message).

Also, what are the next steps we should take to find the cause of love.quit not working reliably?

@MikuAuahDark
Copy link
Contributor

Ok so it looks like SDL blocks the event poll before LOVE have a chance to poll or process for the "focus" event. Thanks for testing.

As for the love.quit case, unfortunately it's hit-or-miss. Android documentation specifically mentions that issue.

Note

Do not count on this method being called as a place for saving data! For example, if an activity is editing data in a content provider, those edits should be committed in either onPause() or onSaveInstanceState(Bundle), not here. This method is usually implemented to free resources like threads that are associated with an activity, so that a destroyed activity does not leave such things around while the rest of its application is still running. There are situations where the system will simply kill the activity's hosting process without calling this method (or any others) in it, so it should not be used to do things that are intended to remain around after the process goes away.

So yeah, for now the best workaround is to save on love.focus. Combining with that SDL hint, perform love.timer.sleep(0.1) in the love.update when the window isn't focused to limit it to 10 FPS in the background.

I'll need to think on how to proceed, if it's something workable in LOVE or SDL upstream.

@MikuAuahDark MikuAuahDark added the library dependency Related to a library used by LÖVE label Mar 27, 2025
@Grench6
Copy link
Author

Grench6 commented Mar 27, 2025

As for the love.quit case, unfortunately it's hit-or-miss.

Fair enough, love.focus will do the trick for me.

Combining with that SDL hint, perform love.timer.sleep(0.1) in the love.update when the window isn't focused to limit it to 10 FPS in the background.

Thanks for the tip!

I'll need to think on how to proceed, if it's something workable in LOVE or SDL upstream.

Okay, if you think of something or would like help with more testing just ping me.

Have a great day.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Android library dependency Related to a library used by LÖVE
Projects
None yet
Development

No branches or pull requests

3 participants