【Android】Android JNI

文章目录

  • 1、简介
    • 1)java调用native接口
    • 2)native调用java接口
  • 2、jni.h
    • 1)基本类型
    • 2)C++中的非基本类型
    • 3)C中的非基本类型
    • 4)变量field与函数method
    • 5)函数签名
    • 6)引用类型
    • 7)一个很重要的函数结构
    • 8)两个很重要的数据类型:JNIEnv和JavaVM,C和C++的实现不同。
    • 9)其它
  • 3、jni原理
  • 4、Android jni
  • 5、AndroidRuntime

1、简介

JNI,Java Native Interface,用于Java与C/C++相互调用的接口,下面举例说明其用法。

1)java调用native接口

第一步,将需要本地实现的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

2)native调用java接口

在上面介绍的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

2、jni.h

下面来看看jni.h中定义了哪些内容。

1)基本类型

/* 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;

2)C++中的非基本类型

/*
 * 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;

3)C中的非基本类型

/*
 * 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;

4)变量field与函数method

struct _jfieldID;                       /* opaque structure */
typedef struct _jfieldID* jfieldID;     /* field IDs */

struct _jmethodID;                      /* opaque structure */
typedef struct _jmethodID* jmethodID;   /* method IDs */

5)函数签名

typedef union jvalue {
    jboolean    z;
    jbyte       b;
    jchar       c;
    jshort      s;
    jint        i;
    jlong       j;
    jfloat      f;
    jdouble     d;
    jobject     l;
} jvalue;

6)引用类型

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是支持跨线程使用的。

7)一个很重要的函数结构

typedef struct {
    const char* name;
    const char* signature;
    void*       fnPtr;
} JNINativeMethod;

8)两个很重要的数据类型:JNIEnv和JavaVM,C和C++的实现不同。

#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、线程管理等。

9)其它

最后就是一些jni相关的宏定义。

3、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的函数。

4、Android jni

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函数完成注册工作。

5、AndroidRuntime

在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完成注册。

你可能感兴趣的:(Android知识库)