This is a continuation of Part I in which we talked about sharing a common code base written in C with Android and iPhone applications. In this part, we will discuss the changes required to get the application to get up and running on Android.

There are many things you can do to improve code reuse, and restructure your application to be more portable. For a code base where most of the logic is written in C, we are going to be using the Java Native Interface (JNI). The most important parts of using JNI is to call System.loadLibrary() to link the Java source code with the C source code, and declare the native methods that you are going to use. If you don’t do this, then several errors will get thrown.

The static code block tells the Android Java runtime to load libCube.so from the library load path, which includes .apk archive directory in which your application is located during deployment. The native declarations tells the Android Java runtime to bind methods to those found in libCube.so, since this is not done automatically. However, the JNI file in which these methods are defined can be generated using the javah utility. After the javah utility has generated the .c and .h files, then you must fill in the implementations manually.

Below are the implementations of the touch functions in JNI:

You can see that the internal CubeWorld object is represented in the Java source code by an int, even though it is a pointer in the C source code. The reason for this is that Java doesn’t support pointer types, so neither does JNI. However, Android currently runs primarily on 32-bit architectures, and so we can safely store a pointer in an int on these architectures. On 64-bit architectures we would use long instead.

The next step is to ensure that the C source code is built into the target library libCube.so. In order to do this, we are going to follow the guidelines set by the ndk-build tool that comes with the Android Native Development Kit (NDK). It comes with a large library of Makefiles that are fine-tuned for targeting the Android platform, and using those Makefiles is the recommended way to build native Android apps. Some of the Makefile templates of interest are the following:

  • BUILD_STATIC_LIBRARY (rarely used)
  • BUILD_SHARED_LIBRARY (most common)
  • BUILD_EXECUTABLE (useful for non-graphical tools)

Since JNI requires that we use a shared library, we will write the Makefile with that template in mind. The actual template can be found at {YourAndroidNDK}/build/core/build-shared-library.mk if you would like more information. The following is an example of {YourProject}/jni/Android.mk:

Now that we’ve got the sources in order, how do we build them? This is much easier than it used to be, now all you have to do is call ndk-build and ant.

There are other build styles as well. For example, you can run ndk-build from a terminal, then use Eclipse (which uses ant) to install to a testing device. Using a combination of the Android NDK and JNI we have abstracted most of our code base by writing it in C, and loading the shared library in Java to bridge between the application logic, and Android’s GUI and graphics system. This follows the Don’t Repeat Yourself (DRY) principle of never copying when you can reuse. It is important to realize that we did this for code reuse, not for performance. Google has made it clear that the NDK will not benefit most apps, because Dalvik’s JIT is getting better every day. So abstracting our code into C is not a performance demand, it’s simply an implementation choice.

Stay tuned for Part III, in which we discuss how to bind the common code base to Objective-C to get the iOS/iPhone application up and running.