I both didn't explain this very well in my Android Dev in C post, and I've learned a bunch going through docs and android_native_app_glue.h, so I thought this deserved it's own post.

android_native_app_glue.h(anag.h), is the closest thing to docs for the native c glue layer on Android. I'll try my best to summaraize what I've learned trying to implement it. I'll include line numbers from anag.h just in case you'd like to reference anag.h directly. In cases where the function/struct/variable is referenced multiple times, I'll do my best to point to the most relevant line for each context.

I am in no way a professional Android developer, in fact, this is my first time trying and Android programming, so please always use anag.h as the source of truth.

App Entry

"The application must provide a function named "android_main()" that will be called when the activity is created. android_main() receives a pointer to a valid android_app structure that contains references to other important objects
e.g. the ANativeActivity object instance the application is running on."

void android_main(struct android_app *app)

This is the entry point. We provide android_main and the glue calls into it when "the activity"(our program) starts. The header mentions "the activity" which is the NativeActivity class from our AndroidManifest.xml.


void android_main(struct android_app *app) 
{
    AppState app_state = {0};
    app_state.current_screen = HOME_SCREEN;
    app_state.current_palette = palette_vintage_peach;
    app->userData = &app_state;

    app->onAppCmd = handle_cmd;
    app->onInputEvent = handle_input;
    LOG("android_main started");

    while (1) 
    {
        int events;
        struct android_poll_source *source;

        // Block until something happens; we only redraw on events.
        while (ALooper_pollOnce(-1, NULL, &events, (void **)&source) >= 0)  
        {
            if (source)
                source->process(app, source);
            if (app->destroyRequested) 
            {
                LOG("destroy requested, exiting");
                return;
            }
        }
    }
}

There's one big change from the first android_main. I'm now creating an AppState right when _main starts and pointing userData at it. AppState is just a struct holding any state my app might need to reference, right now mine is:


typedef struct {
    Screen current_screen;
    Palette current_palette; // this is how I'm letting the user select different color palettes
    InputState input;        // x, y, is_down, and was_down for now
} AppState;

While I suppose you could just make AppState a global for the sake of simplicity, the Android glue layer hands an *app struct(ln113) to both android_main(ln49) and onInputEvent(ln122)(I called mine handle_input), which contains a void* userData(ln113) field. We can store the address of our AppState there to reach it from any callback. It took me a while to wrap my head around this, so I'm going to explain that one more time.

1. I create an AppState struct at the start of android_main AppState state = {0};
2. It stores its address in the glue layer: app->userData = &state;

    (A user touches the screen)

3. The glue layer calls handle_input(app, event)
4. handle_input reads the address back: AppState *s = (AppState *)app->userData;
5. s points at the same state from step 1.

The problem we're trying to solve here is that the glue layer decides what our callbacks recieve. handle_input is handed a struct android_app *app and an AInputEvent *event and that's it(ln 122). Because we can't add an AppState * parameter to the callback, handle_input has no direct way to reach our app's state. The only thing it get's in the app, which means our state has to be reachable through app. That's exactly what userData is for. "The application can place a pointer to its own state object here if it likes."(ln 111)

Input Handling

At this point it's time to handle input. The glue layer calls onInputEvent(ln122) whever there's input. I registered mine as handle_input.


static i32 handle_input(struct android_app *app, AInputEvent *event) 
{
    AppState *state = (AppState *)app->userData;

    if (AInputEvent_getType(event) != AINPUT_EVENT_TYPE_MOTION)
        return 0; // not a touch (keys etc.) — let the system handle it

    i32 action = AMotionEvent_getAction(event) & AMOTION_EVENT_ACTION_MASK;

    state->input.x = AMotionEvent_getX(event, 0);
    state->input.y = AMotionEvent_getY(event, 0);

    switch (action) 
    {
        case AMOTION_EVENT_ACTION_DOWN:
            state->input.is_down = 1;
            state->input.clicked = 0;
            break;
        case AMOTION_EVENT_ACTION_UP:
            state->input.is_down = 0;
            state->input.clicked = 1;
            break;
        case AMOTION_EVENT_ACTION_MOVE:
            break;
    }

    draw_frame(app);
    return 1;
}

First di pull my AppState back out of userData, followed by some guards.

This callback recieves every kind of input, including keypresses, which we don't want to deal with. the getType check filters to motion events. We return 0 to rell the system we didn't handle the event.

getAction

Log Printing Macro

I got sick of typing out __android_log_print(ANDROID_LOG_INFO, "APPNAME", "string"); so I wrote a macro to help:

#define LOG(...) __android_log_print(ANDROID_LOG_INFO, "notes", __VA_ARGS__)
It lives in my ever expanding types.c, which does include some types, but also just includes whatever code I share between projects.
Here's the docs for Android Logging for reference: Logging

"Comments quoted from android_native_app_glue.h, part of the Android NDK, Apache 2.0."

← Back to all posts