The Android SDK
The Android SDK is strictly for Java Programmers — however, the Native Development Kit cracks the door to let some “C” inside
The supported and prescribed manner of creating Android applications is via the Android SDK and that means writing your applications in Java.
But what if you have a large body of code already written in C and you want to take leverage that investment for your Android efforts ? Should you port your code to Java ? Porting your code may be the right answer, but before you start refactoring your code into Java, you should have a look at the Android Native Development Kit (NDK).
Introduced around the release of Android version 1.5, the Android NDK permits developers to write code in “C” that is then callable from Android applications written in Java. The plumbing between the environments is known as the Java Native Interface, or JNI.
JNI has been around for years as a means to permit Java developers to access vendor SDKs or other available C code. Early on, the majority of software vendors’ SDKs were provided as C language static or dynamic libraries — however, this didn’t do Java programmers much good.
The solution to providing the functionality of those SDKs to Java applications was to write a “wrapper” dll in C. The wrapper implemented the Java Native Interface and then proxies calls to the third-party dll. Over time as Java became more popular, some thoughtful vendors began shipping their libraries Java-ready by providing their own JNI wrappers. Today Android developers can leverage C code with JNI with the help of the NDK.
Building a JNI app
We’ll start by building our JNI C code, implementing the two methods of interest. Here is the C code.
#include <string.h> #include <jni.h> jstring Java_com_msi_mymodule_jnisample_LMJNI_stringFromJNI( JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello from Imalogic !"); } jint Java_com_msi_mymodule_jnisample_LMJNI_incrementFromJNI(JNIEnv* env, jobject thiz, jint innumber) { return innumber + 1; }
The methods are named according to the following pattern:
Java_<the fully qualified Java name space with “.” replaced with “_”>_methodname
In our Java code shown below, the Java package name is com.msi.mymodule.jnisample and the class name is LMJNI. Therefore the function prefix is: “Java_com_msi_mymodule_jnisample_LMJNI_”
Once the code is written, we also need a couple of Makefile snippets to grease the skids in the build process.
In our Android Java application, the application has two TextView (edit box) fields and two buttons, organized into pairs. The first pair is used to exercise the “get a string” JNI function and the second is used for the “increment a number” function.
package com.msi.mymodule.jnisample; import android.app.Activity; import android.os.Bundle; import android.view.View; import android.widget.TextView; import android.widget.Button; public class LMJNI extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button btnGetString = (Button) this.findViewById(R.id.btnGetString); btnGetString.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { TextView label = (TextView) findViewById(R.id.TheLabel); label.setText(stringFromJNI()); // calling a JNI function here! } }); Button btnAddNumber = (Button) this.findViewById(R.id.btnAddNumber); btnAddNumber.setOnClickListener(new Button.OnClickListener(){ public void onClick(View v) { TextView numberField = (TextView) findViewById(R.id.number); int operand = Integer.parseInt(numberField.getText().toString()); int answer = incrementFromJNI(operand); // calling a JNI function here! numberField.setText("" + answer); } }); } // declare the externally provided methods public native String stringFromJNI(); public native int incrementFromJNI(int number); static { // load our JNI library. Note, leave off the "lib" and the ".so" System.loadLibrary("mymodule"); } }