一、JNI简介
JNI 是Java Native Interface的缩写,表示"Java本地调用"。通过JNI技术可以实现Java调用C程序和C程序调用Java代码。
二、JNI函数注册
2.1 静态注册:
静态注册的方式我们平时用的比较多。我们通过javac和javah编译出头文件,然后再实现对应的cpp文件的方式就是属于静态注册的方式。这种调用的方式是由于JVM按照默认的映射规则来匹配对应的native函数,如果没匹配,则会报错
//Java 层代码JniSdk.java
public class NativeDemo {
static {
System.loadLibrary("test_jni");
}
public native String showJniMessage();
}
//Native层代码 jnidemo.cpp
extern "C"
JNIEXPORT jstring JNICALL Java_NativeDemo_showJniMessage
(JNIEnv* env, jobject job) {
return env->NewStringUTF("hello world");
}
JNIEXPORT :在Jni编程中所有本地语言实现Jni接口的方法前面都有一个"JNIEXPORT",这个可以看做是Jni的一个标志,至今为止没发现它有什么特殊的用处。
jstring :这个学过编程的人都知道,当然是方法的返回值了,对应java的String类型,无返回值就是void
JNICALL :这个可以理解为Jni 和Call两个部分,和起来的意思就是 Jni调用XXX(后面的XXX就是JAVA的方法名)。
Java_NativeDemo_sayHello:这个就是被上一步中被调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。
JNIEnv * env:这个env可以看做是Jni接口本身的一个对象,jni.h头文件中存在着大量被封装好的函数,这些函数也是Jni编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象。例如:env->GetObjectClass()。(详情请查看jni.h)
jobject obj:代表着native方法的调用者,本例即new NativeDemo();但如果native是静态的,那就是NativeDemo.class .
也就是说,我们的native sayHello()方法实际上是运行C的Java_NativeDemo_sayHello()这个方法,我们是不能随意写C函数名的的,只能这样写。
2.2 动态注册
动态注册不再按照特定的规则去实现native函数,只要在.c文件里面根据对应的规则声明函数即可,所以我们可以不用默认的映射规则,直接由我们告诉JVM,java的native函数对应的是C文件里面的哪个函数。
//Java 层代码JniSdk.java
public class JniSdk {
static {
System.loadLibrary("test_jni");
}
public static native int numAdd(int a, int b);
public native void dumpMessage();
}
//Native层代码 jnidemo.cpp
JNINativeMethod g_methods[] = {
{"numAdd", "(II)I", (void*)add},
{"dumpMessage","()V",(void*)dump},
};
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
j_vm = vm;
JNIEnv *env = NULL;
if (vm->GetEnv((void**)&env, JNI_VERSION_1_2) != JNI_OK) {
LOGI("on jni load , get env failed");
return JNI_VERSION_1_2;
}
jclass clazz = env->FindClass("com/example/dragon/androidstudy/jnidemo/JniSdk");
//clazz对应的类名的完整路径。把.换成/ g_methods定义的全局变量 1 是g_methods的数组长度。也可以用sizeof(g_methods)/sizeof(g_methods[0])
jint ret = env->RegisterNatives(clazz, g_methods, 2);
if (ret != 0) {
LOGI("register native methods failed");
}
return JNI_VERSION_1_2;
}
上面的JNI_OnLoad函数是在我们通过System.loadlibrary函数的时候,JVM会回调的一个函数,我们就是在这里做的动态注册的事情,通过env->RegisterNatives注册。
这里主要讲解一下g_methods对象,下面是JNINativeMethods结构体的定义
typedef struct {
const char* name; //对应java中native的函数名
const char* signature; //java中native函数的函数签名
void* fnPtr; //C这边实现的函数指针
} JNINativeMethod;
其中signature指的是函数的签名
三、函数签名
3.1 什么是函数签名:
所谓函数签名,简单点的理解可以理解成一个函数的唯一标识,一个签名对应着一个函数的签名。这个是一一对应的关系。有些人可能会问:函数名不能作为标识么?答案当然是否定的
3.2 为什么需要函数的签名:
我们知道,java是支持函数重载的。一个类里面可以有多个同名但是不同参数的函数,所以函数名+参数名才唯一构成一个函数标识,因此我们需要针对参数做一个签名标识。这样jni层才能唯一识别到一个函数
3.3 如何获取函数的签名
函数的签名是针对函数的参数以及返回值进行组成的。它遵循如下格式(参数类型1;参数类型2;参数类型3.....)返回值类型。例如我们上面的numAdd函数一样。他在java层的函数声明是
3.4 public static native int numAdd(int a, int b);
两个参数都是int,并且返回值也是int,所以的函数签名是(II)I。
3.5 public native void dumpMessage();
而dumpMessage函数没有任何参数,并且返回值也是空,所以它的签名是()V
3.6 函数类型对应的签名的映射关系:
类型标识 Java类型
Z boolean
B byte
C char
S short
I int
J long
F float
D double
L/java/language/String String
[I int[]
[Ljava/lang/object Object[]
V void
四、JNIEnv
JNIEnv贯穿了整个JNI技术的核心,java层调用native函数主要通过映射关系的建立,但jni函数调用java层的函数就要通过JNIEnv了
4.1 何为JNIEnv:
JNIEnv是JVM内部维护的一个和线程相关的代表JNI环境的结构体,这个结构体是和线程相关的。并且C函数里面的线程与java函数中的线程是一一对应关系。也就是说,如果在java里的某个线程调用jni接口,不管调用多少个JNI接口,传递的JNIEnv都是同一个对象。因为这个时候java只有一个线程,对应的JNI也只有一个线程,而JNIEnv是跟线程绑定的,因此也只有一个
4.2 通过JNIEnv调用java对象方法,通过JNIEnv调用方法大致可以分为以下两步:
a、获取到对象的class,并且通过class获取成员属性
b、通过成员属性设置获取对应的值或者调用对应的方法
public class JniSdk {
private int mIntArg = 5;
public int getArg() {
return mIntArg;
}
}
void dump(JNIEnv *env, jobject obj) {
LOGI("this is dump message call: %p", obj);
jclass jc = env->GetObjectClass(obj);
jmethodID jmethodID1 = env->GetMethodID(jc,"getArg","()I");
jfieldID jfieldID1 = env->GetFieldID(jc,"mIntArg","I");
jint arg1 = env->GetIntField(obj,jfieldID1);
jint arg = env->CallIntMethod(obj, jmethodID1);
LOGI("show int filed: %d, %d",arg, arg1);
}
这边需要注意的一点是:如果jni方法是通过static方式调用的话,这边的jobject表示的是jclass对象,需要进行强转,并不表示一个独立的对象
4.3 跨线程如何调用java方法
a,上面可以直接调用的原因是java调用到jni层的时候始终都在同一个线程,因此再jni层可以直接操作从java层传递下来的JNIEnv对象来实现各种操作。但是如果是在JNI层创建的一个额�
没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
收起资源包目录
Jni.zip (51个子文件)
Jni
gradle.properties 1KB
gradle
wrapper
gradle-wrapper.jar 58KB
gradle-wrapper.properties 236B
gradlew.bat 3KB
build.gradle 745B
nativelib
src
androidTest
java
com
bob
nativelib
ExampleInstrumentedTest.java 753B
test
java
com
bob
nativelib
ExampleUnitTest.java 378B
main
java
com
bob
nativelib
NativeLib.java 368B
JNITools2.java 606B
JNITools.java 604B
TestCallBack.java 481B
JNI说明.txt 16KB
cpp
CMakeLists.txt 3KB
nativelib.cpp 561B
dynamicnativelib.c 1KB
jnitojava.cpp 668B
dynamicnativelib2.cpp 925B
AndroidManifest.xml 153B
proguard-rules.pro 750B
libs
build.gradle 1KB
consumer-rules.pro 0B
.gitignore 6B
settings.gradle 758B
local.properties 417B
jniapp
src
androidTest
java
io
jayx
jniapp
ExampleInstrumentedTest.java 742B
test
java
io
jayx
jniapp
ExampleUnitTest.java 375B
main
java
io
jayx
jniapp
MainActivity.java 1KB
res
mipmap-xxhdpi
ic_launcher_round.webp 6KB
ic_launcher.webp 3KB
mipmap-hdpi
ic_launcher_round.webp 3KB
ic_launcher.webp 1KB
drawable-v24
ic_launcher_foreground.xml 2KB
mipmap-anydpi-v26
ic_launcher.xml 272B
ic_launcher_round.xml 272B
values-night
themes.xml 835B
mipmap-mdpi
ic_launcher_round.webp 2KB
ic_launcher.webp 982B
mipmap-xxxhdpi
ic_launcher_round.webp 8KB
ic_launcher.webp 4KB
mipmap-xhdpi
ic_launcher_round.webp 4KB
ic_launcher.webp 2KB
values
colors.xml 378B
strings.xml 68B
themes.xml 835B
layout
activity_main.xml 778B
drawable
ic_launcher_background.xml 5KB
AndroidManifest.xml 789B
proguard-rules.pro 750B
libs
build.gradle 1KB
.gitignore 6B
gradlew 6KB
共 51 条
- 1
资源评论
12345,catchatiger
- 粉丝: 201
- 资源: 12
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功