【Android】Android JNI

悠悠 2022-02-04 02:34 520阅读 0赞

文章目录

  • 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方法。

  1. // TestJNI.java
  2. class TestJNI
  3. {
  4. private native int testJniAdd(int v1, int v2); // using native to declaration
  5. }

第二步,使用javac命令编译java类,如下面例子中编译 TestJNI.java 后生成TestJNI.class文件。

  1. javac TestJNI.java

第三步,使用javah生成.h头文件,如下面例子中生成的TestJNI.h文件。

  1. javah TestJNI
  2. // TestJNI.h
  3. /* DO NOT EDIT THIS FILE - it is machine generated */
  4. #include <jni.h>
  5. /* Header for class TestJNI */
  6. #ifndef _Included_TestJNI
  7. #define _Included_TestJNI
  8. #ifdef __cplusplus
  9. extern "C" {
  10. #endif
  11. /*
  12. * Class: TestJNI
  13. * Method: testJniAdd
  14. * Signature: (II)I
  15. */
  16. JNIEXPORT jint JNICALL Java_TestJNI_testJniAdd(JNIEnv *, jobject, jint, jint);
  17. #ifdef __cplusplus
  18. }
  19. #endif
  20. #endif // _Included_TestJNI

从上面自动生成的代码可以看出,jni有一定的规则,一个是数据类型和宏定义,这个是C/C++跨平台的惯例,再一个是函数签名规则,下面会介绍。
第四步,在本地代码中实现native方法,如下面例子中TestJNI.c的Java_TestJNI_testJniAdd。

  1. // TestJNI.c
  2. #include <stdio.h>
  3. #include "TestJNI.h"
  4. JNIEXPORT jint JNICALL Java_TestJNI_testJniAdd(JNIEnv *env, jobject obj, jint v1, jint v2)
  5. {
  6. int ret = v1 + v2;
  7. printf("%s called, %d + %d = %d\n", __func__, v1, v2, ret);
  8. return v1 + v2;
  9. }

第五步,编译上述的本地方法,生成动态链接库,如下面例子中使用gcc命令编译生成libjnitest.so。
gcc -fpic -shared -o libjnitest.so TestJNI.c -I
第六步,在Java类中加载这一动态链接库,如下面例子使用System.loadLibrary(“jnitest”)。

  1. // TestJNI.java
  2. class TestJNI
  3. {
  4. private native int testJniAdd(int v1, int v2); // using native to declaration
  5. static
  6. {
  7. System.loadLibrary("jnitest"); // load native lib with xxx of libxxx.so from the right path
  8. }
  9. }

第七步,Java代码中的其它地方可以正常调用这一native方法,完整代码如下所示。

  1. // TestJNI.java
  2. class TestJNI
  3. {
  4. private native int testJniAdd(int v1, int v2); // using native to declaration
  5. private void test()
  6. {
  7. System.out.println("called native from java: testJniAdd(10, 11) is " + testJniAdd(10, 11));
  8. }
  9. public static void main(String args[])
  10. {
  11. new TestJNI().test();
  12. }
  13. static
  14. {
  15. System.loadLibrary("jnitest"); // load native lib with xxx of libxxx.so from the right path
  16. }
  17. }

第八步,运行,首先需要导出前面生成的native lib路径,然后使用java命令运行,如下面例子中native lib库和运行java命令在同一目录。

  1. export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
  2. java TestJNI

最后,运行结果如下。

  1. Java_TestJNI_testJniAdd called, 10 + 11 = 21
  2. 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方法。

  1. // TestJNI.java
  2. class TestJNI
  3. {
  4. private native int testJniAdd(int v1, int v2); // using native to declaration
  5. private void test()
  6. {
  7. System.out.println("called native from java: testJniAdd(10, 11) is " + testJniAdd(10, 11));
  8. }
  9. public int negate(int v)
  10. {
  11. int ret = -v;
  12. System.out.println("negate called from native: " + ret);
  13. return ret;
  14. }
  15. public static void main(String args[])
  16. {
  17. new TestJNI().test();
  18. }
  19. static
  20. {
  21. System.loadLibrary("jnitest"); // load native lib with xxx of libxxx.so from the right path
  22. }
  23. }
  24. // TestJNI.c
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include "TestJNI.h"
  28. JNIEXPORT jint JNICALL Java_TestJNI_testJniAdd(JNIEnv *env, jobject obj, jint v1, jint v2)
  29. {
  30. printf("%s called\n", __func__);
  31. jclass cls = (*env)->FindClass(env, "TestJNI");
  32. jmethodID mid = (*env)->GetMethodID(env, cls, "negate", "(I)I");
  33. jint ret = (*env)->CallIntMethod(env, obj, mid, 12);
  34. printf("called java from native: negate(12) is %d\n", ret);
  35. return v1 + v2;
  36. }

最后,运行结果如下。

  1. Java_TestJNI_testJniAdd called
  2. called java from native: negate(12) is -12
  3. called native from java: testJniAdd(10, 11) is 21

2、jni.h

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

1)基本类型

  1. /* Primitive types that match up with Java equivalents. */
  2. typedef uint8_t jboolean; /* unsigned 8 bits */
  3. typedef int8_t jbyte; /* signed 8 bits */
  4. typedef uint16_t jchar; /* unsigned 16 bits */
  5. typedef int16_t jshort; /* signed 16 bits */
  6. typedef int32_t jint; /* signed 32 bits */
  7. typedef int64_t jlong; /* signed 64 bits */
  8. typedef float jfloat; /* 32-bit IEEE 754 */
  9. typedef double jdouble; /* 64-bit IEEE 754 */
  10. /* "cardinal indices and sizes" */
  11. typedef jint jsize;

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

  1. /*
  2. * Reference types, in C++
  3. */
  4. class _jobject {};
  5. class _jclass : public _jobject {};
  6. class _jstring : public _jobject {};
  7. class _jarray : public _jobject {};
  8. class _jobjectArray : public _jarray {};
  9. class _jbooleanArray : public _jarray {};
  10. class _jbyteArray : public _jarray {};
  11. class _jcharArray : public _jarray {};
  12. class _jshortArray : public _jarray {};
  13. class _jintArray : public _jarray {};
  14. class _jlongArray : public _jarray {};
  15. class _jfloatArray : public _jarray {};
  16. class _jdoubleArray : public _jarray {};
  17. class _jthrowable : public _jobject {};
  18. typedef _jobject* jobject;
  19. typedef _jclass* jclass;
  20. typedef _jstring* jstring;
  21. typedef _jarray* jarray;
  22. typedef _jobjectArray* jobjectArray;
  23. typedef _jbooleanArray* jbooleanArray;
  24. typedef _jbyteArray* jbyteArray;
  25. typedef _jcharArray* jcharArray;
  26. typedef _jshortArray* jshortArray;
  27. typedef _jintArray* jintArray;
  28. typedef _jlongArray* jlongArray;
  29. typedef _jfloatArray* jfloatArray;
  30. typedef _jdoubleArray* jdoubleArray;
  31. typedef _jthrowable* jthrowable;
  32. typedef _jobject* jweak;

3)C中的非基本类型

  1. /*
  2. * Reference types, in C.
  3. */
  4. typedef void* jobject;
  5. typedef jobject jclass;
  6. typedef jobject jstring;
  7. typedef jobject jarray;
  8. typedef jarray jobjectArray;
  9. typedef jarray jbooleanArray;
  10. typedef jarray jbyteArray;
  11. typedef jarray jcharArray;
  12. typedef jarray jshortArray;
  13. typedef jarray jintArray;
  14. typedef jarray jlongArray;
  15. typedef jarray jfloatArray;
  16. typedef jarray jdoubleArray;
  17. typedef jobject jthrowable;
  18. typedef jobject jweak;

4)变量field与函数method

  1. struct _jfieldID; /* opaque structure */
  2. typedef struct _jfieldID* jfieldID; /* field IDs */
  3. struct _jmethodID; /* opaque structure */
  4. typedef struct _jmethodID* jmethodID; /* method IDs */

5)函数签名

  1. typedef union jvalue {
  2. jboolean z;
  3. jbyte b;
  4. jchar c;
  5. jshort s;
  6. jint i;
  7. jlong j;
  8. jfloat f;
  9. jdouble d;
  10. jobject l;
  11. } jvalue;

6)引用类型

  1. typedef enum jobjectRefType {
  2. JNIInvalidRefType = 0,
  3. JNILocalRefType = 1,
  4. JNIGlobalRefType = 2,
  5. JNIWeakGlobalRefType = 3
  6. } jobjectRefType;

jni支持三种引用类型,Local、Global和Weak Global。引用有自己的的生命周期,Local是自释放的,Global和Weak Global需要手动释放。Local和Global引用的对象不会被gc回收,而Weak Global引用的对象可以被gc回收。Local是线程独立的,只能在创建它的线程使用,而Global和Weak Global是支持跨线程使用的。

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

  1. typedef struct {
  2. const char* name;
  3. const char* signature;
  4. void* fnPtr;
  5. } JNINativeMethod;

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

  1. #if defined(__cplusplus)
  2. typedef _JNIEnv JNIEnv;
  3. typedef _JavaVM JavaVM;
  4. #else
  5. typedef const struct JNINativeInterface* JNIEnv;
  6. typedef const struct JNIInvokeInterface* JavaVM;
  7. #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。

  1. enum StartMode {
  2. Zygote,
  3. SystemServer,
  4. Application,
  5. Tool,
  6. };

在AndroidRuntime有四个重要的回调,可以看出AndroidRuntime的四个阶段,分别是onVmCreated、onStarted、onZygoteInit、onExit,代码如下。

  1. /**
  2. * This gets called after the VM has been created, but before we
  3. * run any code. Override it to make any FindClass calls that need
  4. * to use CLASSPATH.
  5. */
  6. virtual void onVmCreated(JNIEnv* env);
  7. /**
  8. * This gets called after the JavaVM has initialized. Override it
  9. * with the system's native entry point.
  10. */
  11. virtual void onStarted() = 0;
  12. /**
  13. * This gets called after the JavaVM has initialized after a Zygote
  14. * fork. Override it to initialize threads, etc. Upon return, the
  15. * correct static main will be invoked.
  16. */
  17. virtual void onZygoteInit() { }
  18. /**
  19. * Called when the Java application exits to perform additional cleanup actions
  20. * before the process is terminated.
  21. */
  22. virtual void onExit(int /*code*/) { }

在AndroidRuntime的start函数中,通过startReg注册jni,代码如下。

  1. void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
  2. {
  3. /*
  4. * Register android functions.
  5. */
  6. if (startReg(env) < 0) {
  7. ALOGE("Unable to register all android natives\n");
  8. return;
  9. }
  10. }

在startReg中调用register_jni_process进行注册。

  1. /*
  2. * Register android native functions with the VM.
  3. */
  4. /*static*/ int AndroidRuntime::startReg(JNIEnv* env)
  5. {
  6. if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
  7. env->PopLocalFrame(NULL);
  8. return -1;
  9. }
  10. }

这里有个关键的变量gRegJNI,定义如下。

  1. static const RegJNIRec gRegJNI[] = {
  2. REG_JNI(register_com_android_internal_os_RuntimeInit),
  3. REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),
  4. REG_JNI(register_android_os_SystemClock),
  5. REG_JNI(register_android_util_EventLog),
  6. REG_JNI(register_android_util_Log),
  7. // ...
  8. };

REG_JNI是个宏,其中的参数就是个函数名,分布在各个文件,在AndroidRuntime.cpp中使用extern进行了声明,如下代码。

  1. extern int register_android_os_Binder(JNIEnv* env);
  2. extern int register_android_os_Process(JNIEnv* env);
  3. extern int register_android_graphics_Bitmap(JNIEnv*);
  4. extern int register_android_graphics_BitmapFactory(JNIEnv*);
  5. extern int register_android_graphics_BitmapRegionDecoder(JNIEnv*);
  6. // ...

下面是REG_JNI宏的定义,其实就是个函数指针的类型定义。

  1. #define REG_JNI(name) { name }
  2. struct RegJNIRec {
  3. int (*mProc)(JNIEnv*);
  4. };

然后在register_jni_procs中循环调用gRegJNI数组中各个mProc即对应的函数名,从而完成jni注册,如下代码。

  1. static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
  2. {
  3. for (size_t i = 0; i < count; i++) {
  4. if (array[i].mProc(env) < 0) {
  5. #ifndef NDEBUG
  6. ALOGD("----------!!! %s failed to load\n", array[i].mName);
  7. #endif
  8. return -1;
  9. }
  10. }
  11. return 0;
  12. }

在各个文件的jni注册函数中,调用了core_jni_helpers.h中的RegisterMethodsOrDie,接着调用了AndroidRuntime::registerNativeMethods,然后调用了libnativehelper的JNIHelp.cpp中的jniRegisterNativeMethods,最终调用jnit本身的RegisterNatives完成注册。

发表评论

表情:
评论列表 (有 0 条评论,520人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Android JNI

    ![20210320191621381.png][] 在Android OS上开发应用程序,Google提供了两种开发包:SDK和NDK。Android 平台从一开就已经支持

    相关 JNI

    JNI是Java Native Interface的缩写,它提供了若干的[API][]实现了Java和其他语言的通信(主要是[C][]&[C++][C 1])。从Java1.1

    相关 JNI

    JNI是Java Native Interface的缩写,译为Java本地接口。它允许Java代码和其他语言编写的代码进行交互。在android中提供JNI的方式,让Java程

    相关 JNI介绍

    JNI介绍 Java 本机接口(Java Native Interface (JNI))是一个本机[编程][Link 1]接口,它是 Java 软件开发工具箱(Java

    相关 java JNI

    本文转载总结至 http://www.cnblogs.com/mandroid/archive/2011/06/15/2081093.html  不得不承认以前的开发人员的

    相关 java JNI

    本文转载总结至 http://www.cnblogs.com/mandroid/archive/2011/06/15/2081093.html  不得不承认以前的开发人员的