This is a heads up for those Android developers that use the NDK (Native Development Kit) to write Android apps. If you are using the NDK, you are almost certainly using the glue code that Google provides in the android_native_app_glue.c file.
Chances are that in your Google Play Developer Console, you see reports of Application Not Responding (ANR keyDispatchingTimedOut.) For my app, I have 756 of these reports on an installed base of 1.5M downloads. Consulting stackoverflow or other developer groups, will invariably yield the advice not to block the main thread. However, it is easy to cause this ANR without blocking the main thread, if you are using the android_native_app_glue.c file in your project.
If two events are generated at exactly the same time, using different sources or devices, the app will freeze. You can easily produce this with a PS3 controller hooked up to your Android device and depress both analogue sticks at exactly the same time, or release them at exactly the same time. If you do this while running an NDK based app, the app will freeze and issue an ANR.
It took me a day of debugging to find a work around for this, but I am happy to report that the following change to the glue code will stop the issue from happening. What you need to do is get events from the queue repeatedly in a loop, instead of just handling a single event in process_input() function.
static void process_input(struct android_app* app, struct android_poll_source* source)
{
AInputEvent* event = NULL;
while ( AInputQueue_hasEvents( app->inputQueue ) )
{
if ( AInputQueue_getEvent( app->inputQueue, &event ) >= 0 )
{
int32_t handled = 0;
uint32_t devid = AInputEvent_getDeviceId( event );
uint32_t src = AInputEvent_getSource( event );
//LOGV("New input event: type=%d devid=%x src=%x\n", AInputEvent_getType(event), devid, src);
int32_t predispatched = (AInputQueue_preDispatchEvent(app->inputQueue, event));
if (app->onInputEvent != NULL && !predispatched) handled = app->onInputEvent(app, event);
if (!predispatched) AInputQueue_finishEvent(app->inputQueue, event, handled);
}
}
}
I have reported the issue to Google.