文章目录

  • 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 <jni.h>
/* 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 <stdio.h>
#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<jni.h所在目录>
第六步,在Java类中加载这一动态链接库,如下面例子使用System.loadLibrary(“jnitest”)。

// TestJNI.java
class TestJNI
{private native int testJniAdd(int v1, int v2); // using native to declarationstatic{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 declarationprivate 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 declarationprivate 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 <stdio.h>
#include <stdlib.h>
#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<String8>& 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 NDEBUGALOGD("----------!!! %s failed to load\n", array[i].mName);
#endifreturn -1;}}return 0;
}

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

【Android】Android JNI相关推荐

  1. 【分享】Android JNI实例​

    [分享]Android JNI实例​ Android的SDK中没有包括JNI的支持,而且对如何支持JNI也没有任何文档说明.不过既然整个Android平台是开源的,我们可以通过Google发布的源代码 ...

  2. 【NFC】Android NFC API Reference中英文

    SkySeraph 博客园 首页 新随笔 联系 订阅 管理 随笔- 192  文章- 0  评论- 441  [NFC]Android NFC API Reference中英文 [NFC]Androi ...

  3. 【Android】Android Studio 1.5+ 中混合调试Native和Java代码

    [Android]Android Studio 1.5+ 中调试Native和Java代码 Android Studio 1.5+表示Android Studio 1.5版本以及以上. 网上大部分中文 ...

  4. 【朝花夕拾】Android性能篇之(二)Java内存分配

    前言       原文:[朝花夕拾]Android性能篇之(二)Java内存分配        在内存方面,相比于C/C++程序员,咱们java系程序员算是比较幸运的,因为对于内存的分配和回收,都交给 ...

  5. 【译】Android系统简介—— Activity

    续上一篇,继续介绍Android系统.上一篇: [译]Android系统简介 本文主要介绍构建Android应用的一些主要概念: Activity Activity是应用程序中一个单独的有UI的页面( ...

  6. 【Android】Android 设置Activity窗体 不显示标题和全屏显示

    [一]Android 设置Activity窗体 不显示标题 android:theme="@android:style/Theme.NoTitleBar" 1 <activi ...

  7. 【OkHttp】Android 项目导入 OkHttp ( 配置依赖 | 配置 networkSecurityConfig | 配置 ViewBinding | 代码示例 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  8. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发机制...

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  9. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  10. 【Android】Android 集成商米内置打印机打印票据

    文章目录 [Android]Android 集成商米内置打印机打印票据 1.集成商米打印依赖 2.规范接口接口 3.使用到的相关对象以及工具类 4.MainActivity初始化接口 5.Uniapp ...

最新文章

  1. 总结Linux-ubuntu基本配置方法(远程连接,数据库,jdk,tomcat......)
  2. sicily 1012. Stacking Cylinders
  3. c++20 协程 图片识别框架 紫丁香
  4. KubeSphere
  5. 职场中怎么看待上级“发火”的问题?
  6. Picasso detected an unsupported OkHttp on the ...
  7. 千氪|比特币十周年大事记
  8. quartz2D简单使用
  9. Silverlight 功能其全的PDF 控件
  10. 非法关机linux分辨率丢失,非法关机造成文件系统损坏,怎么办?请教:附图片...
  11. Spring Session
  12. 2022年上半年系统集成项目管理工程师下午真题及答案解析
  13. 洛谷4173(fft带通配符字符串匹配)
  14. Linux和Windows双系统下,找回丢失Windows启动项
  15. 趣图:看到网友晒了新抱枕,我也想换个新的了
  16. 白话ArcGIS系列软件技术应用(一)空间地理数据库的创建
  17. 【XBOX360】Xbox360 RGH3.0 刷机教程
  18. MySQL表结构的管理
  19. 最适合freshman的Java习题集(四)数组
  20. Video Of You! 勒索诈骗邮件

热门文章

  1. DS18b20温度值换算
  2. 基于PHP+小程序(MINA框架)+Mysql数据库的干洗店洗衣小程序系统设计与实现
  3. 手把手教你做音乐播放器(五)音乐列表的存储(上)
  4. java项目宕机出现原因,java服务宕机原因查询
  5. uni-app 页面生命周期
  6. Recovery HBOOT SPL RADIO APP2SD 金卡
  7. 借游戏带动“卖铲”收益,“卖铲子”的Unity借元宇宙起飞?
  8. 基因表达微阵列数据分类的多目标启发式算法
  9. AtCoder Beginner Contest 242 C~E 题解
  10. 《惢客创业日记》2019.03.24(周日)《惢客》011 目前的市场现状是什么样的?(二)