Pure C or C++ native activity in Android


#1

Hi there,

I saw in SFML that it’s possible (since Android API level >= 9 IIRC) that you can directly start a NDK application using a shared library (so no Java involved at all). This facilitate even more the creation of pure C or C++ based game.

However, the actual Java bridge in the SDL examples is quite complex and do many things, so I wonder if there is a way we move this to the C part and thus avoid any Java glue at all.

Somebody already tried that?

Just for information, here’s how it’s done in SFML




#2

That’s not true. Java must be used. It is simply that SFML is using a
Java Activity class called “NativeActivity”, which was provided by
Google. And the rest of the communication they write to Java is using
JNI on the native side.

Once upon a time before that, it was sample code and everybody copied
the ideas from NativeActivity because this was/is the only way to get
to native code and NativeActivity wasn’t officially part of Android.

While I have my own gripes about SDLActivity, I have more gripes with
NativeActivity. And after you account for all the things SDL
does/supports, you will find that your NativeActivity implementation
needs to be subclassed (and starts looking like SDLActivity again) or
you will write a ton of JNI code on the C-side.

(My biggest gripe with SDLActivity is that it runs all the NDK stuff
on a background thread instead of the main thread. This makes it
really hard to interact with any Android APIs because you cannot
directly make native calls because you are essentially a second class
citizen when you are on a background thread. So integrating things
like In-App Purchases or the zillion of other services Android users
expect nowadays is really freaking hard. But NativeActivity is no
better in this regard.
What I would like to do is write an optional new alternative
SDLActivity which can be substituted in and instead works from the
main thread. It should also be simpler because some of the complexity
in the current implementation is due to the fact that it runs on a
background thread. Since SDL already has support/hooks for platforms
like Emscripten/web that don’t let you manually drive the event loop,
this alternative activity can leverage this.)


#3

Whilst SDL2 “supports” platforms which don’t give you control of the event loop, some (I would guess many) applications cannot practically be adapted to work with that restriction.

My app is one. I need full control of the event loop because under certain circumstances I need to guarantee a very low latency response to events. In those circumstances I busy-wait in a tight loop calling SDL_PollEvent (using 100% of a core) for perhaps several tens of milliseconds.

If SDL2’s Android implementation were to be changed so that such busy-waiting in the main event loop was not permissible, my app simply wouldn’t run.


#4

This is why I said I it would be an optional/alternative SDLActivity
that you would swap in. I know there is too much existing legacy code
of people that depend on the existing behavior. But for those that
don’t have this problem, this other activity implementation could just
be substituted in. (But I’m so buried with other things, it isn’t
clear when/if I’ll get to this.)

Also keep in mind that since Android doesn’t really let you poll the
event loop and you are on a background thread, it is not so clear if
your tight polling actually would give you any advantage over using
the native platform’s frame event callback. Because SDL is faking the
event loop polling by listening events that come in on the main Java
thread, and then pushing them into a thread safe SDL event queue which
can then be read by thread when the OS scheduler decides to yield
(which can and has varied greatly between different Android versions),
there is enough context switching and potential locking, JNI bridge
crossing, and other non-deterministic delays that will creep
in-between the time this all reaches the SDL_PollEvent that make it
really hard to say if this is any faster on Android.


#5

Subjectively it gives as much advantage as on the other platforms I support (Windows, MacOS, Linux, iOS). I imagine it will depend in part on whether the thread in which I am doing the tight polling has most of a CPU core to itself.

Whether I might be able to use the native platform’s frame event callback depends on whether it’s acceptable, occasionally, to spin in that callback for more than a frame period (say 50 ms) before returning. If so it might work, but if there’s a strict requirement for the callback to return in less than 16 ms I can’t live with that.