1.JNI介绍
JNI全称Java Native Interface,是Java和C/C++之间通信的接口,它允许Java应用程序调用和被native代码调用。JNI提供了一种机制,使得Java应用程序可以与使用其他编程语言编写的native库进行交互。
通过JNI,Java应用程序可以调用使用C、C++、Objective-C等编程语言编写的native库中的函数。这对于需要访问底层系统资源、使用特定硬件功能或利用其他语言实现的库的Java应用程序非常有用。
在JNI中,Java代码可以声明native方法,这些方法是使用Java语言编写的函数的声明,但实际上它们的实现是在native代码中完成的。native代码可以是使用其他编程语言编写的代码,例如C或C++。
2.JNI类型和数据结构
2.1Java 原始类型及其对应的Native类型
Java类型 | Native类型 | 描述 |
boolean | jboolean | 无符号 8 位 |
byte | jbyte | 有符号 8 位 |
char | jchar | 无符号 16 位 |
short | jshort | 有符号 16 位 |
int | jint | 有符号 32 位 |
long | jlong | 有符号 64 位 |
float | jfloat | 32位 |
double | jdouble | 64位 |
void | void | NA |
2.2签名类型
JNI的签名类型对应的Java类型为:
签名类型 | java类型 |
Z | boolean |
B | byte |
C | char |
S | short |
I | int |
J | long |
F | float |
D | double |
L fully-qualified-class ; | fully-qualified-class |
[ type | type[] |
( arg-types ) ret-type | method type |
例如,以下方法
long f (int n, String s, int[] arr);
它的签名为:
(ILjava/lang/String;[I)J
I:表示的是第一个参数类型int
Ljava/lang/String;:表示第二个参数类型是String
[I:表示第三个参数类型为int[]
J:表示返回值为long类型
3.JNI注册方式
JNI函数的注册方式分为静态注册和动态注册
3.1静态注册
静态注册一般是在java文件里定义一个native方法,然后在对应的cpp文件里实现就可以了,JVM会按照默认映射规则去找对应的native方法。
java文件代码:
package com.example.jindemo
Public class JniTest {
static
{
System.loadLibrary ("jni_test");//加载so库
}
public native void testJniMethod();
}
JniTest类在加载的时候会加载静态代码块,调用System.loadLibrary()方法来加载native库jni_test,在这个库中实现了testJniMethod()方法。
C/C++文件代码:
jnitest.cpp
#include <jni.h>
extern "C" {
/**
* Native (JNI) implementation of JniTest.testJniMethod()
*/
JNIEXPORT void JNICALL Java_com_example_jindemo_JniTest_testJniMethod(
JNIEnv* env, jobject obj) {
}
}
上面例子中java文件中JniTest.testJniMethod()方法对应C/C++文件的Java_com_example_jindemo_JniTest_testJniMethod方法,在调用Java层的native方法时,JVM会从JNI库中寻找默认的方法并调用,按照如下规则匹配对应的C/C++函数:
Java_方法的完整路径(中间用下划线隔开)
上面testJniMethod的完整路径就是com.example.jindemo.JniTest.testJniMethod,所以会匹配Java_com_example_jindemo_JniTest_testJniMethod方法。
3.2动态注册
虽然静态注册比较简单,但是如果修改了java层的包名或者类名,那么也要修改对应的C/C++文件。
而动态注册不需要按照特定的规则去实现native方法,只需要在C/C++文件里声明对应的函数即可,相当于直接告诉JVM,java层对应的native函数是C/C++文件里的哪个函数。
以原生PowerManagerService.java里的native方法为例:
frameworks/base/services/core/java/com/android/server/power/PowerManagerService.java
private native void nativeInit();
private static native void nativeAcquireSuspendBlocker(String name);
private static native void nativeReleaseSuspendBlocker(String name);
private static native void nativeSetAutoSuspend(boolean enable);
private static native void nativeSetPowerBoost(int boost, int durationMs);
private static native boolean nativeSetPowerMode(int mode, boolean enabled);
private static native boolean nativeForceSuspend();
com_android_server_power_PowerManagerService.cpp里声明对应的规则:
frameworks/base/services/core/jni/com_android_server_power_PowerManagerService.cpp
static const JNINativeMethod gPowerManagerServiceMethods[] = {//方法对应关系表
/* name, signature, funcPtr */
{"nativeInit", "()V", (void*)nativeInit},
{"nativeAcquireSuspendBlocker", "(Ljava/lang/String;)V",
(void*)nativeAcquireSuspendBlocker},
{"nativeForceSuspend", "()Z", (void*)nativeForceSuspend},
{"nativeReleaseSuspendBlocker", "(Ljava/lang/String;)V",
(void*)nativeReleaseSuspendBlocker},
{"nativeSetAutoSuspend", "(Z)V", (void*)nativeSetAutoSuspend},
{"nativeSetPowerBoost", "(II)V", (void*)nativeSetPowerBoost},
{"nativeSetPowerMode", "(IZ)Z", (void*)nativeSetPowerMode},
};
int register_android_server_PowerManagerService(JNIEnv* env) {
int res = jniRegisterNativeMethods(env, "com/android/server/power/PowerManagerService",
gPowerManagerServiceMethods, NELEM(gPowerManagerServiceMethods));
(void) res; // Faked use when LOG_NDEBUG.
LOG_FATAL_IF(res < 0, "Unable to register native methods.");
// Callbacks
jclass clazz;
FIND_CLASS(clazz, "com/android/server/power/PowerManagerService");
GET_METHOD_ID(gPowerManagerServiceClassInfo.userActivityFromNative, clazz,
"userActivityFromNative", "(JIII)V");
// Initialize
for (int i = 0; i <= USER_ACTIVITY_EVENT_LAST; i++) {
gLastEventTime[i] = LLONG_MIN;
}
gPowerManagerServiceObj = NULL;
return 0;
}
Java与JNI通过JNINativeMethod的结构来建立联系,结构如下:
typedef struct {
const char* name;
const char* signature;
void* fnPtr;
} JNINativeMethod;
name:对应Java文件中函数的名字
signature:函数签名,对应了函数的参数和返回值
fnPtr:指向对应的C函数
{"nativeInit", "()V", (void*)nativeInit},的意思是java中的nativeInit函数,对应C库中的nativeInit函数,这个函数没有参数,并且没有返回值。
然后通过jniRegisterNativeMethods()方法注册,第二个参数对应着java类,即com/android/server/power/PowerManagerService;第三个参数为方法对应关系表。
看下register_android_server_PowerManagerService这个方法是在哪里调用的:
frameworks/base/services/core/jni/onload.cpp
xtern "C" jint JNI_OnLoad(JavaVM* vm, void* /* reserved */)
{
JNIEnv* env = NULL;
jint result = -1;
if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) {
ALOGE("GetEnv failed!");
return result;
}
ALOG_ASSERT(env, "Could not retrieve the env!");
register_android_server_broadcastradio_BroadcastRadioService(env);
register_android_server_broadcastradio_Tuner(vm, env);
register_android_server_PowerManagerService(env);
....
}
当java通过System.loadLibrary加载完JNI动态库后,同时会调用C库中的JNI_OnLoad的函数
,从而使java层可以通过调用声明的方法,来调用对应的C库中的方法。
这个库是在SystemServer里加载的:
frameworks/base/services/java/com/android/server/SystemServer.java
private void run() {
TimingsTraceAndSlog t = new TimingsTraceAndSlog();
try {
t.traceBegin("InitBeforeStartServices");
......
// Initialize native services.
System.loadLibrary("android_servers");
.......
}
4.JNIEnv
JNIEnv全称Java Native Interface Environment,Java本地接口环境。JNIEnv主要用于native函数调用java层函数、变量、对象等。
JNIEnv是和线程绑定的,只能在创建它的线程中使用,不能跨线程传递,因此,不同线程的JNIEnv是彼此独立的。
4.1获取JNIEnv
在同一个线程下可以直接通过参数获取JNIEnv,参考上述静态注册的例子
jnitest.cpp
#include <jni.h>
extern "C" {
/**
* Native (JNI) implementation of JniTest.testJniMethod()
*/
JNIEXPORT void JNICALL Java_com_example_jindemo_JniTest_testJniMethod(
JNIEnv* env, jobject obj) {
}
}
java层调用jni函数的时候始终处于同一线程,所以jni可以直接操作从java层传递过来的JNIEnv对象。
跨进程使用AttachCurrentThread()方法重新创建JNIEnv:
JNIEnv* env = AttachCurrentThread();
4.2JNIEnv的主要方法
JNIEnv主要用于native函数调用java层函数、变量、对象等,它的主要方法有:
函数名 | 功能 |
jclass FindClass(JNIEnv *env, const char *name) | 获取类 |
jclass GetObjectClass(JNIEnv *env, jobject obj) | 返回对象的类 |
jmethodID GetMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) | 返回类或接口的实例(非静态)方法的方法 ID |
jmethodID GetStaticMethodID(JNIEnv *env, jclass clazz, const char *name, const char *sig) | 返回类的静态方法的方法 ID |
jobject NewGlobalRef(JNIEnv *env, jobject obj) | 创建对 obj 参数所引用的对象的新全局引用 |
jobject NewObject() | 构造一个新的 Java 对象 |
jstring NewString(JNIEnv *env, const jchar *unicodeChars, jsize len) | 从 Unicode 字符数组构造一个新的 java.lang.String 对 |
其他方法参考: