As you may know, the NDK allows you to develop Android apps in C or C++, which allows you to reuse existing code, or write cross-platform code with relative ease.  One piece of the NDK puzzle that was difficult to wrap my head around was how exactly my NDK app could receive Android lifecycle events if those calls are going through Java.  Well, it turns out there are three distinct ways (that I’m aware of) to solve this problem, which will free you up a bit to focus on your app code instead of the inner-workings of the Android OS.

Those three methods are: using JNI through a Java Activity, using the NativeActivity class directly, and using the NativeActivity class indirectly through the “android_native_app_glue” library.

JNI Through a Java Activity

This is the most straight-forward approach to receiving Android lifecycle events.  Apps that use the Android SDK exclusively subclass the Activity class and override the various lifecycle events, like so:

public class SimpleActivity extends Activity {

    protected void onCreate(Bundle savedInstanceState) {
    }

    protected void onPause() {
    }

    protected void onResume() {
    }
    //and so on...
}

When you want to handle your lifecycle events in native code, you can use JNI to pass the Java-side events to your native code.

public class SimpleNativeTestActivity extends Activity {

    //*** Load native library.
    static {
	System.loadLibrary("my_native_library");
    }

    //*** Native function declarations.
    private native void onPauseNative();
    private native void onResumeNative();

    protected void onPause() {
        onPauseNative();
    }

    protected void onResume() {
        onResumeNative();
    }
    //and so on...
}

As you can see, when Android OS calls these lifecycle functions on the Java side, we can just pass the call and data along to the native side of the app, assuming you’ve created a library with the native functions in JNI format. Note that I’m not totally sure that the onCreateNative call would work as is – passing a “Bundle” to native code sounds like a crash in the making! I’m still learning JNI parameter passing myself, so take that with a grain of salt.

Why would you solve the problem this way instead of using a NativeActivity? Well, it turns out that NativeActivity was first supported in API level 9, while the NDK was introduced way back in API level 3. So, the only way to get these lifecycle events for API levels 3-8 is to pass them through the Activity class.

What about performance?  If you check out the source code for NativeActivity, you’ll see that it is essentially doing the same thing that I just did above – a JNI call to native code when a Java lifecycle event is received!  There doesn’t seem to be a performance gain to using NativeActivity over Activity; it is purely for convenience.  When you use NativeActivity, there is a bunch of code involved in passing Android data (like Bundles) across the native boundary that you no longer have to worry about.

Use NativeActivity

If you are targeting API level 9 and above, you can use NativeActivity instead of Activity to save yourself a little bit of trouble with passing lifecycle events and data through JNI.

The first mental hurdle I had to overcome is that you DO NOT need to inherit from NativeActivity to create an app, which is different from what you would do with a regular Activity.  You certainly can inherit from NativeActivity, but find a compelling reason to do so first. Instead of inheriting from NativeActivity, you just tell the manifest that you will be using the NativeActivity class.  Then, you can pass in a piece of metadata that tells NativeActivity what shared library to use.  Here is an example manifest file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.myapp.mycompany"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="9" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:hasCode="false" >
        <activity
            android:name="android.app.NativeActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <meta-data android:name="android.app.lib_name" android:value="my_native_library" />
        </activity>
    </application>
</manifest>

Four important things to note here. First, the minSdkVersion must be at least 9 for NativeActivity to be available. Second, the application tag must have the “android:hasCode” attribute set to “false”. This is telling Android that there is no Java code associated with this app (unless, of course, you wrote some). Third, the name of the activity is “android.app.NativeActivity” – this is the default NativeActivity class.  And finally, there is a meta-data tag as a child of the activity tag.  This meta-data is a key/value pair that tells NativeActivity what library contains the C/C++ code for starting the app (I’ll give an example of such code below).  The library you specify here is, of course, a library that you’ve compiled using the “ndk-build” tool, and it should be in your “lib” directory.

So with this manifest, NativeActivity will start up, load the library you’ve specified in the meta-data, and call the function “ANativeActivity_onCreate” in that library.  If the function doesn’t exist in the library, the app will crash on startup.

Here is a very simple example of such a library:

static void onResume(ANativeActivity* activity) {
    //*** Do something when you resume.
}

void ANativeActivity_onCreate(ANativeActivity *activity, void *savedState, size_t savedStateSize) {
    //*** Set lifecycle callbacks.
    activity->callbacks->onResume = onResume;
    //and so on...
}

That’s all there is to it; you will receive the lifecycle callbacks in native code now, with no Java code required.

A problem that both the Activity and NativeActivity have is that, by default, you are setting yourself up to do processing on the main thread, since lifecycle events are fired on the main thread. As a result, you might accidentally perform a blocking operation in a lifecycle callback, which can cause an “Application Not Responding” error. This approach can be fine for some applications, and you could even use your own custom solution for spawning threads to deal with this issue.  However, there is a ready-made solution to this problem that might work for you: the android_native_app_glue library.

Using android_native_app_glue

As the name sort of implies…in a way…I guess…android_native_app_glue provides a convenient adhesive between the NativeActivity in the previous section and a clean framework for building native apps.  The glue library takes care of some tedious code that you’d probably have to do for your NativeActivity app anyway, which is nice.

What is even nicer is that glue provides a basic framework that catches lifecycle events on the main thread and notifies you about them on another thread using a message pump.  This means you can worry less about reacting to a lifecycle event with a blocking operation.  Not to say that this will necessarily make your life easier since threads often make things more complicated.

You can find the source code for glue under $(NDK-ROOT)/sources/android/native_app_glue or here.  Examining this can give you a better idea of how the multithreading works and why it may be a better solution than just using NativeActivity alone.  It basically adds state and lifecycle information to a shared data location, so that you can pick up that data with a different thread (basically, a message pump).

Let’s get the basics up and running.  You start by setting up your app’s manifest the same way as the NativeActivity method above.  Using the glue library is just an extension to the NativeActivity approach.

Start with the following C code in your library:

#include <android_native_app_glue.h>

void android_main(struct android_app* state) {
    //*** Stop glue from being stripped.
    app_dummy();
}

By including “android_native_app_glue.h”, you are essentially adding a wrapper around the NativeActivity stuff we did above and telling Android to do all that stuff.  android_native_app_glue registers the lifecycle callbacks, sets everything up, and then calls android_main(…) when it is ready to go.

The app_dummy() line is required because when you build the library, the glue library can get stripped out during optimization if no functions from the library are called.  To get around this, you are provided a bit of a hack – a dummy function that you call to ensure the library gets included.  Make sure you include this line or you’ll get compiler errors later!

You also need to modify your “Android.mk” build file in the “jni” folder:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := my_native_library
LOCAL_SRC_FILES := mylibfile.cpp

LOCAL_STATIC_LIBRARIES := android_native_app_glue

include $(BUILD_SHARED_LIBRARY)

$(call import-module,android/native_app_glue)

You must include the android_native_app_glue library file. I’m not totally sure what that last line does, but it seems to be required for everything to compile correctly.  Another possibility is to just copy the header and source for android_native_app_glue into your project and compile them with your code; either way will work.

If you compile and run your app, everything should work, but we aren’t actually capturing lifecycle events yet.  There is one last thing to be done for that.  Add the following code to your library:

#include <android_native_app_glue.h>
static void handle_cmd(struct android_app* app, int32_t cmd) {

    //*** Handle each command.
    switch(cmd) {

    case APP_CMD_GAINED_FOCUS:
        break;

    case APP_CMD_LOST_FOCUS:
        break;
    }
}

void android_main(struct android_app* state) {

    //*** Stop glue from being stripped.
    app_dummy();

    //*** Register app command callback.
    state->onAppCmd = handle_cmd;

    while (1) {
        //*** Handle application events.
        int ident;
        int events;
        struct android_poll_source* source;

        while ((ident=ALooper_pollAll(0, NULL, &events, (void**)&source)) >= 0) {
            if (source != NULL) {
                source->process(state, source);
            }
        }
    }
}

The infinite while loop above will run for the duration of the app. The only thing it does right now is listen for application events and pump them through as they arrive. The first parameter of ALooper_pollAll is interesting – it says how long to wait for an event to arrive before continuing. In games, you probably want this to be 0 so you aren’t blocking if there are no events to process. If you set it to -1, it will always block until an event occurs!

Right before the while loop, we register the onAppCmd callback.  The wording is different, but this still boils down to lifecycle events.  When the Looper receives an event that is lifecycle relevant, this function is called and passed the relevant data, where you can handle the lifecycle event as needed.

Conclusion

Hopefully this post lends some clarity to this particular aspect of NDK app development.  There isn’t too much documentation on the subject (that I could find, at least), so examining the source code is most helpful.

I found it helpful to think about each of these approaches as just building upon one another.  It isn’t easy because the documentation makes them look like completely different approaches to the problem, and the naming conventions are different in each approach.  But realize that NativeActivity just builds upon Activity by passing the lifecycle events to native code for you.  And then android_native_app_glue just takes the data passed by NativeActivity and provides you with a framework for getting lifecycle events in a more traditional way.  You could easily rewrite or modify NativeActivity or android_native_app_glue to customize the functionality to your specific needs, or just use them as a starting point for your own lifecycle framework.

Further Reading and References

http://developer.android.com/reference/android/app/NativeActivity.html – Official documentation on NativeActivity.

http://www.altdevblogaday.com/2011/12/09/running-native-code-on-android-part-1/ – Helpful blog post on the subject.

Tagged with →  
Share →

One Response to Three Ways to Catch Lifecycle Events with NDK

  1. Lior Tal says:

    Nice article, thanks!

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>