从场景和需求梳理JNI接口


1 真实的情景和互动的对象

都有哪些Java和C交互的场景?真实的场景是:
1、一个C程序被系统加载并运行(比如java.exe)
2、C程序加载Java虚拟机并运行一个Java类(这个动作使用Invocation API接口)
3、有可能,这个运行的Java类需要加载一个C库(通过System.loadLibrary函数),C库代码和这个Java类互动,包括互相调用函数和访问变量(这个是通常所言的JNI,通过env指向的函数列表来实现相互操作)

其中第二个场景有互动需求,注意依赖关系是C加载Java。其中第三个场景有互动需求,注意依赖关系是Java加载C。

哪些可以互动?必须是某种实体。
     C侧:
          变量
          函数
     Java侧:
          对象
          对象的公有方法和变量
          类的静态方法和静态变量

上述个体都可能成为互动的对象,只有方法或函数可以成为互动的发起者

至此我们理清楚了互动情景。下面我们来梳理互动的操作细节


2 第三个场景,Java代码加载C代码

根据场景来区分。先研究第三个场景,也即Java代码加载C代码。

2.1 可能的互动

Java代码加载C代码时可能出现的互动:
     Java访问C侧
          看到的范例是Java调用C函数
          Java可以访问C变量吗?未知,细节留待以后研究
          C可以有全局或静态变量吗?可以
          C可以重入吗?未知,取决于C侧代码设计

     C访问Java侧
          C可以通过JNI接口查找和访问Java侧类
          C可以通过JNI接口调用Java侧类构造方法,用以生成对象
          C可以通过JNI接口访问Java侧对象的变量和方法
          C可以通过JNI接口访问Java侧类的静态变量和静态方法

2.2 Java访问C侧

Java侧访问C侧函数的找寻方法:
1、通过函数原型匹配,注意函数原型是通过函数名和其他信息一起组合生成的
2、通过注册函数直接强制链接。注意C库中的JNI_OnLoad()会被自动和首先调用,可以在其中注册和链接

函数原型样式:
     JNIEXPORT void JNICALL Java_HelloJNI_printHello(JNIEnv *, jobject, jstring)

     JNIEnv指向JNI接口的功能函数表,表中各种函数往往用来转换数据,或者访问其他
     如果是C++,可以省略掉这个参数
     jobject指向调用的发出者,说明这里是对象发出对C的调用。如果是类的静态方法在调用C代码的话,这里是jclass。
     jstring是一个参数示例


2.3 C访问Java侧

实质是,plain的C代码怎样访问用对象封装的Java元素
怎样访问一个Java类的变量和方法?首先是这些资源被编码了,并生成了签名,可以根据签名去索引。

可能的访问
     C可以访问JNI接口函数,当然JNI接口函数也就是为C准备的
     C可以通过JNI接口函数查找和访问Java侧类
     C可以通过JNI接口函数调用Java侧类构造方法,用以生成对象
     C可以通过JNI接口函数访问Java侧对象的变量和方法
     C可以通过JNI接口函数访问Java侧类的静态变量和静态方法

有哪些因素在访问中需要考虑?
1、双方有不同的基本数据类型,包括各种char、int、long等等,需要使用一个统一的本地类型:jchar、jint、jlong
2、Java有类和对象的概念,还有String,但C只有plain string,因此C侧需要从Java对象中抽取数据,或组合plain数据为Java对象
以上转换都通过调用JNI接口函数来实现,也即函数原型中“JNIEnv * env”参数所指向的函数列表

3 第二个场景,C代码加载Java代码

在C程序中运行Java类,即Invocation API

两个用途实例:
1、java.exe由C语言编写,通过Invocation API执行Java类
2、Android系统Launcher加载dalvikvm

加载过程一共7步
     1.生成java虚拟机选项
     2.生成java虚拟机
     res = JNI_CreateJavaVM(&vm, (void **)&env, &vm_args);
     3.查找并加载类
     cls = (*env)->FindClass(env, "InvocationApiTest");
     4.获取main()方法的ID
     mid = (*env)->GetStaticMethodID(env, cls, "main", "([Ljava/lang/String;)V");
     5.生成字符串对象,用作main()方法的参数
     6.调用main()方法
     (*env)->CallStaticVoidMethod(env, cls, mid, args);
     7.销毁Java虚拟机
     (*vm)->DestroyJavaVM(vm);


4 一些JNI接口细节

值得关注的有:
1、JNI函数列表里面都有什么?
大体如下三类:
     类、对象、变量、方法等的引用及操作
     字符串、数组操作
     虚拟机接口、异常、C和Java之间函数关联
2、怎样查看方法和变量等的签名?
可以使用javap.exe进行签名。不过看几个范例几乎也能猜出来。

3、局部和全局引用
C侧可以获取Java侧实体的引用,缺省是局部应用,函数退出后即失效。一直有效的话可以使用全局引用。
显然,引用不是一个简单的指向实体的指针,不然怎么会失效呢?

5 参考文档

1、The Java Native Interface Programmer's Guide and Specification
2、Java Native Interface Specification
3、《Android框架揭秘》[韩]金泰延

前面两篇是官方的专业文档,有所有细节,但是看起来头疼且太耗时间,没有必要。《Android框架揭秘》一书对JNI做了很细致很易懂的而且足够的介绍。此书各部分都写得非常仔细,甚至略有啰嗦,各处都有自己的的整理或见解,可以看出作者写书非常的认真尽责,不是拷贝粘贴,所以在此我要大力推荐这本书。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值