Post by rtrussellPost by Daniel GibsonIs there a way to tell Android to wait a little with either "paging out"
the memory or even with switching to another app?
Otherwise I have no idea, but maybe someone who (unlike me) has actual
experience with Android can help
Like you my knowledge of Android is very limited, and to a large extent I'm
relying on the SDL docs to tell me what to do (not entirely unreasonable I
think - it is supposed to be an 'abstraction layer' that isolates the
programmer from platform-specific detail!).
What surprises me is that this seems to be an issue that has not been widely
encountered before. I would have expected worker threads to be the norm
these days - it's effectively the only way to achieve a short latency even
if the UI thread is waiting for VSYNC, and to leverage the power of
multi-core CPUs.
Richard.
Android, and particularly the Android NDK, is awful. You can find
many, many, people attest to this, including John Carmack. I give
talks from time to time on Android development and always remember to
call out how bad the NDK is, and I always get NDK developers thanking
me at the end of the talk for publicly saying what we all know. So you
are entering a rite of passage.
- To your original problem of suspending the thread:
I say don’t suspend your threads and just make them end. Design your
threads so you tell them to quit and you do a wait/join.
Then when your app resumes, start up new threads.
- I would have expected worker threads to be the norm these days
Threads + cross-platform has always been hard. So this does temper
devs to some degree.
SDL’s thread library tries to point you in the right direction by
having a very small set of functions. For example, there is no
SDL_SuspendThread because it is not ubiquitous. (As you discovered,
Android doesn’t want to support it.) But unfortunately, it does not
absolve you from understanding things about the platforms. Threads are
among the worst because there are so many subtle differences and they
really leak serious abstraction details. For example, Emscripten/web
browser does not support threads at all (ignore web workers). And as
far as leaky abstractions, one of the most common pitfalls is many
platforms can’t handle doing certain operations on the non-main
thread, so everybody using threads needs to be very careful about
this.
- app events race condition:
Unfortunately, this is a consequence of threads/leaky abstraction
details, and Android just being terrible. The fundamental problem is
that on Android, SDL is actually running on a background thread. So
when an app suspends, backgrounds, resumes, quits, etc., the events
are happening on the main Android thread, and not the SDL thread.
Android expects you to handle them NOW! But since SDL is on a
background thread, there is a potential race condition.
I have worked on many open source and commercial Android projects, and
every one of them made the same mistake and put their stuff on a
background thread. It didn’t help that Google itself encouraged
developers to do this with their Native Activity example. I have
compared notes with a lot of other Android devs who have to push the
envelope on the NDK too, and we all came to the same conclusion that
Native Activity/background thread is a terrible idea.
Every single project I’ve been on has had some hard edge case race
condition with suspend, background, resume, or quit. I’d actually like
to write a new alternative SDLActivity some day that keeps SDL on the
main thread. This would require some changes on how people deal with
the event loop, but SDL actually already has to do this for
Emscripten, and also has a special function to enable this mode on
iOS, so it actually isn’t unprecedented.
But that aside, I thought SDL will quit correctly on Android with
respect to threads because it basically wait/joins the SDL background
thread to end it, blocking the app on the main thread from quitting
until this happens. (Threads are tricky so maybe there is an edge case
I’m not seeing.)
The bug that I usually see about quitting is due to a very different
stupid, “NDK is terrible”, design problem. Android actually doesn’t
necessarily clean up your NDK side memory. It just leaves it there
untouched. So if you have global or static variables, when you restart
the app, the Android system actually skips re-initializing all your
global and static variables because it considers them already
initialized. So these variables remain at their previous value which
can completely break your application logic.
int g_isRunning = 1;
int main(int arcg, char* argv[])
{
SDL_Init(SDL_VIDEO);
// standard SDL event loop
while(g_isRunning)
{
// handle the event loop which will set the g_isRunning to 0 if the user quits
}
SDL_Quit();
return 0;
}
The first time you launch this, it will work.
Then quit the app.
Then restart the app.
g_isRunning will still be 0 from the last run because Android skips
re-initializing the variable to 1.
Basically, this forces you to be very careful, and always explicitly
reinitialize your globals and statics.
int g_isRunning = 1;
int main(int arcg, char* argv[])
{
// forces reinitialization for subsequent starts
g_isRunning = 1;
SDL_Init(SDL_INIT_VIDEO);
// standard SDL event loop
while(g_isRunning)
{
// handle the event loop which will set the g_isRunning to 0 if the user quits
}
SDL_Quit();
return 0;
}
-Eric