JNI,Java Native Interface,用于Java与C/C++相互调用的接口,下面举例说明其用法。
第一步,将需要本地实现的Java方法加上native声明,如下面例子中TestJNI类中的testJniAdd方法。
// TestJNI.java
class TestJNI
{
private native int testJniAdd(int v1, int v2); // using native to declaration
}
第二步,使用javac命令编译java类,如下面例子中编译 TestJNI.java 后生成TestJNI.class文件。
javac TestJNI.java
第三步,使用javah生成.h头文件,如下面例子中生成的TestJNI.h文件。
javah TestJNI
// TestJNI.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class TestJNI */
#ifndef _Included_TestJNI
#define _Included_TestJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: TestJNI
* Method: testJniAdd
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_TestJNI_testJniAdd(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif // _Included_TestJNI
从上面自动生成的代码可以看出,jni有一定的规则,一个是数据类型和宏定义,这个是C/C++跨平台的惯例,再一个是函数签名规则,下面会介绍。
第四步,在本地代码中实现native方法,如下面例子中TestJNI.c的Java_TestJNI_testJniAdd。
// TestJNI.c
#include
#include "TestJNI.h"
JNIEXPORT jint JNICALL Java_TestJNI_testJniAdd(JNIEnv *env, jobject obj, jint v1, jint v2)
{
int ret = v1 + v2;
printf("%s called, %d + %d = %d\n", __func__, v1, v2, ret);
return v1 + v2;
}
第五步,编译上述的本地方法,生成动态链接库,如下面例子中使用gcc命令编译生成libjnitest.so。
gcc -fpic -shared -o libjnitest.so TestJNI.c -I
第六步,在Java类中加载这一动态链接库,如下面例子使用System.loadLibrary(“jnitest”)。
// TestJNI.java
class TestJNI
{
private native int testJniAdd(int v1, int v2); // using native to declaration
static
{
System.loadLibrary("jnitest"); // load native lib with xxx of libxxx.so from the right path
}
}
第七步,Java代码中的其它地方可以正常调用这一native方法,完整代码如下所示。
// TestJNI.java
class TestJNI
{
private native int testJniAdd(int v1, int v2); // using native to declaration
private void test()
{
System.out.println("called native from java: testJniAdd(10, 11) is " + testJniAdd(10, 11));
}
public static void main(String args[])
{
new TestJNI().test();
}
static
{
System.loadLibrary("jnitest"); // load native lib with xxx of libxxx.so from the right path
}
}
第八步,运行,首先需要导出前面生成的native lib路径,然后使用java命令运行,如下面例子中native lib库和运行java命令在同一目录。
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
java TestJNI
最后,运行结果如下。
Java_TestJNI_testJniAdd called, 10 + 11 = 21
called native from java: testJniAdd(10, 11) is 21
在上面介绍的java调用native接口的基础上,如下面例子,native调用java接口(native的Java_TestJNI_testJniAdd中调用java的negate)时,首先通过JNIEnv FindClass找到java类(TestJNI),然后通过JNIEnv GetMethodID找到java方法(negate),最后通过JNIEnv CallIntMethod调用java方法。
// TestJNI.java
class TestJNI
{
private native int testJniAdd(int v1, int v2); // using native to declaration
private void test()
{
System.out.println("called native from java: testJniAdd(10, 11) is " + testJniAdd(10, 11));
}
public int negate(int v)
{
int ret = -v;
System.out.println("negate called from native: " + ret);
return ret;
}
public static void main(String args[])
{
new TestJNI().test();
}
static
{
System.loadLibrary("jnitest"); // load native lib with xxx of libxxx.so from the right path
}
}
// TestJNI.c
#include
#include
#include "TestJNI.h"
JNIEXPORT jint JNICALL Java_TestJNI_testJniAdd(JNIEnv *env, jobject obj, jint v1, jint v2)
{
printf("%s called\n", __func__);
jclass cls = (*env)->FindClass(env, "TestJNI");
jmethodID mid = (*env)->GetMethodID(env, cls, "negate", "(I)I");
jint ret = (*env)->CallIntMethod(env, obj, mid, 12);
printf("called java from native: negate(12) is %d\n", ret);
return v1 + v2;
}
最后,运行结果如下。
Java_TestJNI_testJniAdd called
called java from native: negate(12) is -12
called native from java: testJniAdd(10, 11) is 21
下面来看看jni.h中定义了哪些内容。
/* Primitive types that match up with Java equivalents. */
typedef uint8_t jboolean; /* unsigned 8 bits */
typedef int8_t jbyte; /* signed 8 bits */
typedef uint16_t jchar; /* unsigned 16 bits */
typedef int16_t jshort; /* signed 16 bits */
typedef int32_t jint; /* signed 32 bits */
typedef int64_t jlong; /* signed 64 bits */
typedef float jfloat; /* 32-bit IEEE 754 */
typedef double jdouble; /* 64-bit IEEE 754 */
/* "cardinal indices and sizes" */
typedef jint jsize;
/*
* Reference types, in C++
*/
class _jobject {};
class _jclass : public _jobject {};
class _jstring : public _jobject {};
class _jarray : public _jobject {};
class _jobjectArray : public _jarray {};
class _jbooleanArray : public _jarray {};
class _jbyteArray : public _jarray {};
class _jcharArray : public _jarray {};
class _jshortArray : public _jarray {};
class _jintArray : public _jarray {};
class _jlongArray : public _jarray {};
class _jfloatArray : public _jarray {};
class _jdoubleArray : public _jarray {};
class _jthrowable : public _jobject {};
typedef _jobject* jobject;
typedef _jclass* jclass;
typedef _jstring* jstring;
typedef _jarray* jarray;
typedef _jobjectArray* jobjectArray;
typedef _jbooleanArray* jbooleanArray;
typedef _jbyteArray* jbyteArray;
typedef _jcharArray* jcharArray;
typedef _jshortArray* jshortArray;
typedef _jintArray* jintArray;
typedef _jlongArray* jlongArray;
typedef _jfloatArray* jfloatArray;
typedef _jdoubleArray* jdoubleArray;
typedef _jthrowable* jthrowable;
typedef _jobject* jweak;
/*
* Reference types, in C.
*/
typedef void* jobject;
typedef jobject jclass;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jobjectArray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jobject jthrowable;
typedef jobject jweak;
struct _jfieldID; /* opaque structure */
typedef struct _jfieldID* jfieldID; /* field IDs */
struct _jmethodID; /* opaque structure */
typedef struct _jmethodID* jmethodID; /* method IDs */
typedef union jvalue {
jboolean z;
jbyte b;
jchar c;
jshort s;
jint i;
jlong j;
jfloat f;
jdouble d;
jobject l;
} jvalue;
typedef enum jobjectRefType {
JNIInvalidRefType = 0,
JNILocalRefType = 1,
JNIGlobalRefType = 2,
JNIWeakGlobalRefType = 3
} jobjectRefType;
jni支持三种引用类型,Local、Global和Weak Global。引用有自己的的生命周期,Local是自释放的,Global和Weak Global需要手动释放。Local和Global引用的对象不会被gc回收,而Weak Global引用的对象可以被gc回收。Local是线程独立的,只能在创建它的线程使用,而Global和Weak Global是支持跨线程使用的。
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif
JNIEnv是个Java虚拟机中的Runtime变量,如上面的FindClass、GetMethodID、CallIntMethod等,都在JNIEnv中定义;JavaVM则用于Java虚拟机管理相关,如获取JNIEnv、线程管理等。
最后就是一些jni相关的宏定义。
Java运行时需要load本地jni库,最终通过dlopen完成。
jni支持静态注册和动态注册。上面的例子就是静态注册,可以看出,静态注册必须遵循一定的规则,函数名过长,格式为Java_Java类名_Java函数名,前面有JNIEXPORT和JNICALL宏标记,函数参数的第一个参数为JNIEnv*,第二个参数为jobject,后面才是Java中的函数参数,这里的jobject就是对应的Java类,多个class需javah多遍,运行时查找效率不高。
关于动态注册,java在load本地lib时,会调用JNI_OnLoad,对应的还有个JNI_OnUnload,我们可以提供一个java和native的函数映射表,并注册给JVM,这样,JVM就通过函数映射表来调用相关的函数。注册函数为RegisterNatives,对应的反注册的函数为UnregisterNatives。
动态注册使用了上面提到的JNINativeMethod,name表示java中native关键字声明的函数名,
signature表示函数签名,包括参数类型和返回值类型,fnPtr表示对应的native的函数。
Android jni代码位置为:frameworks/base/core/jni。从其中的Android.bp中可以看出,jni编译后的结果为libandroid_runtime动态库。
加载libandroid_runtime的地方有两处,一个在frameworks/native/services/surfaceflingerDdmConnection.cpp中的dlopen,另一个在frameworks/base/tools/preload/loadclass/LoadClass中的loadLibrary。
Android jni使用了动态加载,由AndroidRuntime统一注册,注册时使用了libnativehelper库进行传送,不过最终还是调用的jni本身的RegisterNatives函数完成注册工作。
在AndroidRuntime中定义了启动的四种模式,分别是Zygote、SystemServer、Application和Tool,如下面的enum。
enum StartMode {
Zygote,
SystemServer,
Application,
Tool,
};
在AndroidRuntime有四个重要的回调,可以看出AndroidRuntime的四个阶段,分别是onVmCreated、onStarted、onZygoteInit、onExit,代码如下。
/**
* This gets called after the VM has been created, but before we
* run any code. Override it to make any FindClass calls that need
* to use CLASSPATH.
*/
virtual void onVmCreated(JNIEnv* env);
/**
* This gets called after the JavaVM has initialized. Override it
* with the system's native entry point.
*/
virtual void onStarted() = 0;
/**
* This gets called after the JavaVM has initialized after a Zygote
* fork. Override it to initialize threads, etc. Upon return, the
* correct static main will be invoked.
*/
virtual void onZygoteInit() { }
/**
* Called when the Java application exits to perform additional cleanup actions
* before the process is terminated.
*/
virtual void onExit(int /*code*/) { }
在AndroidRuntime的start函数中,通过startReg注册jni,代码如下。
void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
{
/*
* Register android functions.
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
}
在startReg中调用register_jni_process进行注册。
/*
* Register android native functions with the VM.
*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
env->PopLocalFrame(NULL);
return -1;
}
}
这里有个关键的变量gRegJNI,定义如下。
static const RegJNIRec gRegJNI[] = {
REG_JNI(register_com_android_internal_os_RuntimeInit),
REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
REG_JNI(register_android_os_SystemClock),
REG_JNI(register_android_util_EventLog),
REG_JNI(register_android_util_Log),
// ...
};
REG_JNI是个宏,其中的参数就是个函数名,分布在各个文件,在AndroidRuntime.cpp中使用extern进行了声明,如下代码。
extern int register_android_os_Binder(JNIEnv* env);
extern int register_android_os_Process(JNIEnv* env);
extern int register_android_graphics_Bitmap(JNIEnv*);
extern int register_android_graphics_BitmapFactory(JNIEnv*);
extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
// ...
下面是REG_JNI宏的定义,其实就是个函数指针的类型定义。
#define REG_JNI(name) { name }
struct RegJNIRec {
int (*mProc)(JNIEnv*);
};
然后在register_jni_procs中循环调用gRegJNI数组中各个mProc即对应的函数名,从而完成jni注册,如下代码。
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
for (size_t i = 0; i < count; i++) {
if (array[i].mProc(env) < 0) {
#ifndef NDEBUG
ALOGD("----------!!! %s failed to load\n", array[i].mName);
#endif
return -1;
}
}
return 0;
}
在各个文件的jni注册函数中,调用了core_jni_helpers.h中的RegisterMethodsOrDie,接着调用了AndroidRuntime::registerNativeMethods,然后调用了libnativehelper的JNIHelp.cpp中的jniRegisterNativeMethods,最终调用jnit本身的RegisterNatives完成注册。