Fix SDL2 forcibly blocking apps in background with no way to disable [WARNING, major behavior change!]#1773
Conversation
|
I can understand the why, but it's also against the Android App lifecycle too no? Making it a default is IMO a bad option, and will break any Kivy app currently as you stated. |
|
What? How is this in aagainst the lifecycle? (I really don't get that remark)
Android is a multitasking system, and I just explained above how this actually leads to DATA LOSS in many scenarios. It is IMHO a limiting default pandering to bad code, since the lifecycle does include that the activity is normally not instant terminated if it goes into the background. If kivy can't handle this, then it should be fixed to deal with the lifecycle properly.
|
|
@tito can you maybe explain why would you consider it a bad default if kivy's main loop was adapted first? or is that not possible? I would like to try to understand the position better, maybe we can find a solution |
|
I didn't think that Android would let the app continue to run in the background, I guess I'm wrong or out of date. Is there any particular documentation about it? |
|
Yeah android allows this since at least android 4, see "cached process" here: https://developer.android.com/guide/components/activities/process-lifecycle at least if I get the docs right, "cached" is really just a kinda dumb name for "foreground app gets background event -> is now background process and is then allowed to continue to run for a while if resources allow for it" in fact p4a does that even right now, just not on the UI thread (which is exactly what this pull request fixes) since SDL2 has this "feature" to emulate an ancient non-multitasked alternative by blocking the event poll. if you want to try/verify this, just launch a python thread and print stuff out that'll show up in logcat. you'll see easily that it most likely continues to run if you tab out and may even keep running for hours or days depending on how busy your device is with other stuff, and happily spam the log in the background from your spawned thread. so this is really just a completely artificial and inconsistent (not present on any other platforms apart from maybe ios, which is another big reason why I think this is bad and should be off) blocking behavior of SDL_PollEvent() that goes against the very name of the function |
|
you can btw also see this with p4a's loading screen: if it doesn't show up when re-entering the app then it has actually been legitimately running the entire time (although most likely been in a mostly or fully blocked state thanks to SDL2's behavior/the thing this pull request addresses) |
|
Hello. For a longer running app with service, you suggest to do a Why having this optional like @tito suggested would be a problem ? I'm working on a chat app with a service in background which is already doing battery saving things, and I would like the UI to not do anything so the battery is not affected when user is not on it. I also expect the user to be able to instantly be able to use the UI when she goes back to it, without splash screen or anything taking time. If I get it correctly, it seems that current behaviour of |
First of all, I tested my app without ever Secondly, the splash screen usually shows for merely 2-3 seconds, and that is unpreventable anyway (as in android can make your app do that by unloading it if it wants to even today) - however, what IS preventable is that you don't end up at exactly the same screen as before. And that is also what ruins UX more than anything. What I mean by that is: do proper state serialization, make sure to get the user back exactly to the same place. If you do that, this will have barely any impact. If you don't, your app will be a pain to use anyway on phones where it ends up unloaded so you'll give some lower spec users a bad experience already now.
There is no mechanism for that. We don't have optional patching in p4a right now, it would introduce additional complexity. (edit: at least as far as I am aware, I might be wrong) And what for? If kivy handled this properly (which it currently doesn't, but I'm not suggesting a merge before it does) and you write your app properly, why would you still need it? |
|
As an additional note, there is an upstream bug requesting SDL2 turns this into a runtime hint option. Once that happens it will be quite simple to turn it off manually (one-liner) for anyone who wants to, but right now they very misguidedly made it a compile-time option so that is not currently possible |
yes that's my main concern, and it will need testing. A chat app supposed to be running 24/7 must be super light on battery
Of course it can be killed (by Android or user), but most of time it isn't, and waiting 2/3 seconds (+ time to initialize everything to reproduce the app state) each time you want to send or check message is hardly acceptable IMHO (a chat app is typically moving between front use and paused a lot).
If optional build is not already possible, I don't think it would be too hard to implement, I can try to make a PR for that. I'm worrying about battery usage, if really after Kivy is updated this change doesn't impact battery life at all, I have no problem and don't see the point of optional build indeed. But if battery is impacted even slightly, I think this should be optional.
indeed that would solve the problem :) Note that I think your change and argumentation is perfectly valid, but this definitely needs tests on battery impact, and to be optional if it is impacted. thanks |
The thing is though, isn't kivy running at 60FPS right now even with no animations on screen? Fixing that would also kind of go hand in hand with the changes necessary for this ticket, and that should give you much bigger savings for the time the app is active. In total I would expect that you'd actually have quite some gains in total if kivy implements this sanely! (as in, track all scheduled callbacks and animations and if none is scheduled, sleep as long as possible up to 200ms'ish if the app has been idle in foreground for a longer time) What I'm saying is, I understand even a slight decrease may seem like something horrible, but this may not be the best area to add complexity when there's other more obvious places to address if you want to have significant battery life improvements. It might be more fruitful to push upstream/SDL2 to finally fix this. There is already a ticket, there's just not much activity regarding it so far - edit: upstream ticket is here: https://bugzilla.libsdl.org/show_bug.cgi?id=4230 |
|
There appears to be some movement on the SDL2 bugtracker to maybe make this a hint 😄 😄 😄would be really cool if that worked out, I suppose we might see in a few days! 🎉 |
|
Upstream now got |
Fix SDL2 forcibly blocking apps in background with no way to disable by setting
SDL_ANDROID_BLOCK_ON_PAUSEto 0. This is a major behavior change! I tested this with a non-kivy SDL2 app, but not with kivy. (see below for details)Advantages of
SDL_ANDROID_BLOCK_ON_PAUSEset to1:avoids bad data loss in some situations. example: my app has a save button, but because I coded it properly I keep the UI mostly responsive while saving, but because I'm also avoiding unnecessary threading this is all still going on on the main thread.
Result with the current
SDL_ANDROID_BLOCK_ON_PAUSEset to0: if you press save then press the home button in like <500ms (which is a common user action) you are almost 99% guaranteed the save didn't actually go through for bigger documents, and now SDL2 will block the event loop making the UI update also block, and the entire save will block / not finish. Now if you wait long enough, you also have a really high risk the app will get unloaded without ever having saved, and you have data loss. -> very likely risk of save being completely lost if you tab out quick and then don't tab back in for a day or longerWith this pull request: very unlikely there will ever be an issue. The only remaining risk is that your app runs on a rather low spec phone so it will get instant-unloaded after going to background, or android crashes entirely. Both aren't really common.
allows apps to do basic background stuff without services or threading. this is self-explanatory. you can do things in the background without necessarily using complicated thread code, or even a service. of course the app may get terminated, but unless your app is a major resource hog you can assume to be able to do at least a few do low-risk background activities
Disadvantages / risks:
Naive/unaware frameworks will burn all batteries to death. With this change, whatever is controlling the main loop (e.g. kivy, or the kivi app, or whatever other UI lib, ...) must do adaptive slowing down of the polling to something really slow like 500 milliseconds when in background. If not, this is almost guaranteed to be a major battery waster with a huge difference in battery impact of your app while in background
May cause higher battery usage even if you're moderately smart about it(??) This would actually be interesting to know. I'm not sure how bad a 500 milliseconds wakeup interval still is. I assume it has some remaining impact, but is it much? However, an app can always serialize state & use
sys.exit(0)if that turned out to be a problem. So far, my app running with this hasn't shown up in the battery chart although I'm not sure if/how background apps are tracked. If anyone has more info here that'd be interesting to knowThings to check:
Kivy should be tested for race conditions. This pull request can in theory expose race conditions. (since now you can get all sorts of events while in background rather than none because the event loop just hangs until the app gets focus again.) I tested with a non-kivy SDL2 app (using my own UI facilities) which worked fine, but obviously kivy should also be tested
Kivy should slow down the event loop when in background. if it doesn't do that already, of course. (or if kivy apps usually control the loop - I don't use kivy so I'm not sure about the API design here - then there will need to be a bigger announcement of this change, so that well-behaving apps implement this.)
The docs for kivy should possibly get a recommendation to use
sys.exit()when in background on android. In a battery save chapter, there could be a note that after longer background idle it might be wise for an app to quit if it has truly nothing to do and it serializes its state properly. After all, for longer running tasks it should spawn a service anyway