Jni

Java Native Interface

Introduction

The Java Native Interface (JNI) is a programming framework that allows Java code running in the Java virtual machine (JVM) to call and be called by native applications (programs specific to a hardware and operating system platform) and libraries written in other languages, such as C, C++ and assembly.

Example

User JNI to invoke C/C++

Create the c library

$ cd /my-android-dir/development
$ mkdir hellolib
create hellolib.c and Android.mk

Android.mk is the Makefile for Android and hellolib.c is the implementation of native method.
hellolib.c
#include<jni.h>

#define LOG_TAG "TestLib"                                                                                                                 
#undef LOG
#include<utils/Log.h>

JNIEXPORT void JNICALL Java_com_test_TestHelloLib_printHello(JNIEnv *env,jobject jobj)
{
    LOGD("Hello LIB!\n");
}

In the c code, you need to be careful about the naming rule of JNI (You can use javah to create the header of the library and the function signature).
  • Java_com_test_TestHelloLib_printHello is corresponding to the java code.
    • package's name is com.test.
    • class name is TestHelloLib.
    • native funtion is printHello.

Besides LOGD and #define LOG_TAG "TestLib" is to use Android Logļ¼Œso we can use adb logcat to see the log messages.

Android.mk

LOCAL_PATH := $(call my-dir)                                                                                                              

include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
    hellolib.c
LOCAL_C_INCLUDES := \
    $(JNI_H_INCLUDE)
LOCAL_SHARED_LIBRARIES := \
    libutils

LOCAL_PRELINK_MODULE := false

LOCAL_MODULE := libhello

include $(BUILD_SHARED_LIBRARY)

Android.mk is the Makefile for Android platform. You need to specify the options for detail.

  • LOCAL_SRC_FILES : source codes
  • LOCAL_C_INCLUDES: header files
  • LOCAL_SHARED_LIBRARIES : shared library which need to be linked
  • LOCAL_PRELINK_MODULE: enable or disable prelink
  • LOCAL_MODULE : the target module name
  • BUILD_SHARED_LIBRARY : to compile to dynamic library

Compile into shared library

new way:

$ ndk-build
$ cd /my-android-dir/
$ make libhello
============================================
build/core/main.mk:180: implicitly installing apns-conf_sdk.xml
target thumb C: libhello <= development/hellolib/hellolib.c
target SharedLib: libhello (out/target/product/generic/obj/SHARED_LIBRARIES/libhello_intermediates/LINKED/libhello.so)
target Non-prelinked: libhello (out/target/product/generic/symbols/system/lib/libhello.so)
target Strip: libhello (out/target/product/generic/obj/lib/libhello.so)
Install: out/target/product/generic/system/lib/libhello.so
$

Now we get shared library libhello.so which is under out/target/product/generic/system/lib/libhello.so.

Create a new Activity Java program

$ createactivity --out Test  com.test.TestHelloLib
$ vim Test/src/com/test/TestHelloLib.java

Take care about the comment and codes we add.

TestHelloLib.java

package com.test;                                                                                                                         

import android.app.Activity;
import android.os.Bundle;

public class TestHelloLib extends Activity
{
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        // invoke the native method
        printHello()
    }
    // to load the library we just create, !! notice about the prefix and postfix is deleted
    static {
    // The runtime will add "lib" on the front and ".o" on the end of
        // the name supplied to loadLibrary.
        System.loadLibrary("hello");
    }
    // to declare the native function 
    private native void printHello();
}

Upload and test

Run the emulator and compile the activity. We need to upload the library into /system/lib on emulator.

$ ant
$ adb remount
$ adb push out/target/product/generic/system/lib/libhello.so /system/lib
$ adb install TestHelloLib-debug.apk

Now we can just open adb logcat to check the log message!

User javah to genereate header file

create the header of the c/c++ program

$ javah -classpath ../../../out/host/linux-x86/sdk/android-sdk_eng.command_linux-x86/platforms/android-1.5/android.jar:./bin/classes -jni com.test.TestHelloLib

The header may look like this. You can check the method signature according to the header.

... 
/*
 * Class:     com_test_TestHelloLib
 * Method:    printHello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_test_TestHelloLib_printHello
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

Passing basic type variables

In the previous example, we only invoke method which didn't have any arguments. Now we will show how to pass variable into native method.
First we modify the TestHelloLib.java like the following. We just declare the natvie method add.

package com.test;       

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;                                                                                                                  

public class TestHelloLib extends Activity
{                       
    /** Called when the activity is first created. */
    @Override           
    public void onCreate(Bundle savedInstanceState)
    {                   
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        printHello();   

        long t = add(5,10);
        Log.i("JNI","result = "+Long.toString(t));
    }                   
    static {            
        Log.i("JNI", "Trying to load libhello.so");
        // The runtime will add "lib" on the front and ".o" on the end of
        // the name supplied to loadLibrary.
        System.loadLibrary("hello");
    }                   
    private native void printHello();
    public native long add(long a,long b);
}

Now let's modify the native method implementation. We just add the following into the hellolib.c.

JNIEXPORT jlong JNICALL Java_com_test_TestHelloLib_add(JNIEnv *env, jobject jobj, jlong a, jlong b)
{
    LOGD("add!!\n");
    return a+b;
}

The remaining is the same as previous example. Now you can check the log message from adb logcat!

Reference

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License