2、JNI说明
JNI (Java Native Interface)
1. JAVA调用C
Linux是用C语言写的,可以写一个APP简单调用open,read,write来访问驱动程序;
Android是用Java写的,Java怎么访问C函数?
说明:C语言中实现的和java中对应的函数会多两个参数JNIEnv *env和jobject cls
Java如何调用C库的函数:
1、加载C库;
使用System.loadLibrary方法来加载
eg:public class JNIDemo{
staitc {
//1、加载C库
System.loadLibrary("native");//c代码会生成并提供libnative.so
}
public native void hello();
public static void main(String args[]){
JNIDemo d = new JNIDemo();
//2、map java hello <------>c c_hello ,映射的步骤在C语言中实现
//3、调用
d.hello();
}
}
javac JNIDemo.java来编译java程序
2、找到函数;通过映射eg: Java函数名<------>C函数名
A、隐式建立映射:类a.b.c.d.JNIDemo要调用hello函数,C语言中要实习Java_a_b_c_d_JNIDemo_hello,可以使用指令javac -d JNIDemo.java;javah -jni a.b.c.d.JNIDemo来生成一个JNIDemo.h的头文件,这个头文件里面有类下各个函数的JNI字段描述符和隐时调用时在C语言中应该声明的函数名,a.b.c.d是包(目录)名称a/b/c/d/ ,通过隐式调用的方法可以不用在实现JNI_Onload的方法
B、显示建立:1、实现JNI_OnLoad函数,在java中加载C库时会调用这个函数
2、在JNI_OnLoad中调用RegisterNatives来建立映射
3、调用函数;
RegisterNatives说明:
1、定义一个映射数字JNINativeMethod[],每个数组项含3个成员:
A:Java中调用的函数名;
B:JNI字段描述符,用来说明该函数的返回值和参数
C:C语言实现的本地函数
JNI字段描述符说明如下:
“([Ljava/lang/String;)V” 它是一种对函数返回值和参数的编码。这种编码叫做JNI字段描述符(JavaNative Interface FieldDescriptors)。一个数组int[],就需要表示为这样"[I"。如果多个数组double[][][]就需要表示为这样 "[[[D"。也就是说每一个方括号开始,就表示一个数组维数。多个方框后面,就是数组 的类型。
如果以一个L开头的描述符,就是类描述符,它后紧跟着类的字符串,然后分号“;”结束。
比如"Ljava/lang/String;"就是表示类型String;
"[I"就是表示int[];
"[Ljava/lang/Object;"就是表示Object[]。
JNI方法描述符,主要就是在括号里放置参数,在括号后面放置返回类型,如下:
(参数描述符)返回类型
当一个函数不需要返回参数类型时,就使用”V”来表示。
比如"()Ljava/lang/String;"就是表示String f();
"(ILjava/lang/Class;)J"就是表示long f(int i, Class c);
"([B)V"就是表示void String(byte[] bytes);
“(JC)D”就表示double m2(long l,char c);
Java 类型 |
符号 |
Boolean |
Z |
Byte |
B |
Char |
C |
Short |
S |
Int |
I |
Long |
J |
Float |
F |
Double |
D |
Void |
V |
objects对象 |
以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。比如:Ljava/lang/String;如果是嵌套类,则用$来表示嵌套。例如 "(Ljava/lang/String;Landroid/os/FileUtils$FileStatus;)Z" |
另外数组类型的简写,则用"["加上如表A所示的对应类型的简写形式进行表示就可以了,
比如:[I 表示 int [];[L/java/lang/objects;表示Objects[],另外。引用类型(除基本类型的数组外)的标示最后都有个";"
例如:
"()V" 就表示void Func();
"(II)V" 表示 void Func(int, int);
"(Ljava/lang/String;Ljava/lang/String;)I".表示 int Func(String,String)
2、注册这个数组:(*env)->RegisterNatives(JNIEnv *env,jclass clazz,const JNINativeMethod *methods,jint nMethods)
eg:C语言端举例
#include <jni.h> ///usr/lib/jvm/java-1.7.0-openjdk-amd64/include/下面,env下面的函数在jni.h中都有声明,可以看看
#include <stdio.h>
#if 0 //下面结构体仅是举例
typedef struct{
char *name; //Java里调用的函数名
char *signature; //JNI字段描述符,用来表示Java里调用的函数的参数和返回值类型
void *fnPtr; //C语言实现的本地函数
}JNINativeMethod;
#endif
void c_hello(JNIEnv *env,jobject cls)
{
printf("hello world!\n");
}
static const JNINativeMethod methods[] = {
{"hello","()V",(void *)c_hello},
};
/*System.loadLibrary*/
JNIEXPORT jint JNICALL
JNI_Onload(JavaVM *jvm,void *reserved) //jni.pdf P117有详细介绍怎么写JNI_Onload
{
JNIEnv *env;//提供一些辅助函数
jclass cls;//cls是对Java类或实例的引用,如果是static方法,则cls是对Java类的引用,否则是对类的实例化对象的引用
if((*jvm)->GetEnv(jvm,(void **)&env,JNI_VERSION_1_4){
return JNI_ERR;/*JNI version not supported*/
}
cls = (*env)->FindClass(env,"JNIDemo");
if(cls == NULL){
return JNI_ERR;
}
//2、map java hello <------>c c_hello
if((*env)->RegisterNatives(env,cls,method,1)<0)
return JNI_ERR;
return JNI_VERSION_1_4
}
gcc -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -fPIC -shared -o libnative.so native (通过-I指定去那里找jni.h头文件)
export LD_LIBRARY_PATH=.
java JNIDemo
Java和C库怎么传递参数
1、传递基本数据类型,可以直接使用、直接返回;
2、传递字符串;jni.pdf P39,env下的函数都可以在这个pdf中找到说明
eg:hello函数的参数和返回值都是字符串;
jstring JNICALL c_hello(JNIEnv *env,jobject cls,jstring str)
{
const jbyte *cstr;
cstr = (*env)->GetStringUTFChars(env,str,NULL);
if(cstr == NULL){
return NULL;
}
printf("Get string form java :%s\n",cstr);
(*env)->ReleaseStringUTFChars(env,str,cstr);
return (*env)->NewStringUTF(env,"return from c");
}
3、传递数组jni.pdf P48
eg:hello函数的参数是字符串和返回值是int;
jint c_hello(JNIEnv *env,jobject cls,jintArray arr)
{
jint *carr;
jint i,sum=0
carr= (*env)->GetIntArrayElements(env,arr,NULL);
if(carr== NULL){
return 0;
}
for(i=0;i<(*env)->GetArrayLength(env,arr);i++){
sum += carr[i];
}
(*env)->ReleaseIntArrayElements(env,arr,carr,0);
return sum ;
}
eg:hello函数的参数和返回值都是字符串
jintArray c_hello(JNIEnv *env,jobject cls,jintArray arr)
{
jint *carr;
jint *oarr;
jintArray rarr;
jint i,n=0
carr= (*env)->GetIntArrayElements(env,arr,NULL);
if(carr== NULL){
return 0;
}
n=(*env)->GetArrayLength(env,arr);
oarr = malloc(sizeof(jint)*n);
if(oarr == NULL)
{
(*env)->ReleaseIntArrayElements(env,arr,carr,0);
return 0;
}
for(i=0;i<n;i++){
oarr[i] = carr[n-1-i];
}
(*env)->ReleaseIntArrayElements(env,arr,carr,0);
/*create jintArray*/
rarr = (*env)->NewIntArray(env,n);
if(rarr == NULL){
return 0;
}
(*env)->SetIntArrayRegion(env,rarr,0,n,oarr);
free(oarr);
return rarr;
}
jni.pdf P117
Android JNI知识简介
http://blog.csdn.net/linweig/article/details/5417319
Android JNI(实现自己的JNI_OnLoad函数)
http://jjf19850615.blog.163.com/blog/static/356881472013342153912/
查看"JNI field descriptors" (JNI字段描述符)
javap -s -p Var.class
JNINativeMethod的参数解析
http://carywei.iteye.com/blog/1075647
http://cs.fit.edu/~ryan/java/language/jni.html
http://blog.csdn.net/conowen/article/details/7524744
2. C调用JAVA
jni.pdf P97
http://blog.csdn.net/lhzjj/article/details/26470999
步骤:
a. 创建虚拟机,参考jni.pdf P97
JavaVM* jvm;
JNIEnv* env;
jint create_vm(JavaVM** jvm, JNIEnv** env)
{
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=.";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args);
}
b. 获得class
jclass cls = (*env)->FindClass(env, "Hello");
c. 实例化对象 : 获得构造方法(方法名为"<init>"), 构造参数, 调用方法,对于static的方法,不许要实例化
jmethodID mid = (*env)->GetMethodID(env, cls, "<init>", "()V"); // 获得构造方法
jobject obj = (*env)->NewObject(env, cls, mid); // 实例化对象
d. 调用java方法 : 又分为获得方法ID, 构造参数, 调用方法
//获得方法ID
jmethodID smid = (*env)->GetStaticMethodID(env, cls, "sayhello_to", "(Ljava/lang/String;)I");
jmethodID mid = (*env)->GetMethodID(env, cls, "saygoodbye_to", "(Ljava/lang/String;)I");
//构造参数
jstring jstr = (*env)->NewStringUTF(env, "weidongshan@qq.com");
//调用方法
(*env)->CallStaticVoidMethod(env, jobj, smid, jstr);
ret = (*env)->CallIntMethod(env, jobj, mid, jstr);
e.其他,读取/设置类中的属性:
a. 获得属性ID
b. 读取/设置
jfieldID fid = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
jstring jstr = (*env)->NewStringUTF(env, "Bill");
(*env)->SetObjectField(env, jobj, fid, jstr);
eg:caller.c(调用java中静态方法)
#include <stdio.h>
#include <jni.h>
jint create_vm(JavaVM** jvm, JNIEnv** env)
{
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6; //虚拟机版本
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./"; //虚拟机去哪里找哪些类
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args);
}
int main(int argc,char **argv)
{
JavaVM* jvm;
JNIEnv* env;
jclass cls;
int ret = 0;
jmethodID mid;
/*1、create jave virtual machine*/
if (create_vm(&jvm, &env)) {
printf("can not create jvm\n");
return -1;
}
/*2、get class*/
cls = (*env)->FindClass(env,"Hello");
if (cls == NULL) {
printf("can not find hello class\n");
ret = -1;
goto destroy;
}
/* 3. create object */
/* 4. call method
* 4.1 get method
* 4.2 create parameter
* 4.3 call method
*/
mid = (*env)->GetStaticMethodID(env, cls, "main","([Ljava/lang/String;)V");//main是方面名字,后面的jni字段描述符表示方法的参数及返回值(字段描述符可以通过下面指令获得:javac Hello.java;javap -p -s Hello.class就可以看出各个方法的字段描述符怎么写)
if (mid == NULL) {
ret = -1;
printf("can not get method\n");
goto destroy;
}
(*env)->CallStaticVoidMethod(env, cls, mid, NULL);//后面的NULL处是传递给mid方法的参数
destroy:
(*jvm)->DestroyJavaVM(jvm);
return ret;
}
Hello.java文件
public class Hello {
public static void main(String args[]) {
System.out.println("Hello, world!");
}
public static void sayhello_to(String name) {
}
public static void sayhello_to() {
}
}
javac Hello.java
javap -p -s Hello.class // get Signature
gcc -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o caller caller.c -L /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server -ljvm
LD_LIBRARY_PATH=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server ./caller
eg:caller.c(调用非静态方法)
#include <stdio.h>
#include <jni.h>
jint create_vm(JavaVM** jvm, JNIEnv** env)
{
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args);
}
int main(int argc, char **argv)
{
JavaVM* jvm;
JNIEnv* env;
jclass cls;
int ret = 0;
jmethodID mid;
jmethodID cid;
jobject jobj;
jstring jstr;
int r;
/* 1. create java virtual machine */
if (create_vm(&jvm, &env)) {
printf("can not create jvm\n");
return -1;
}
/* 2. get class */
cls = (*env)->FindClass(env, "Hello");
if (cls == NULL) {
printf("can not find hello class\n");
ret = -1;
goto destroy;
}
/* 3. create object //创建一个对象,实例化,参考jni.pdf P69
* 3.1 get constructor method //获得构造方法,构造方法的名字的<init>,通过jni字段描述符来区分构造方法
* 3.2 create parameters//创建参数
* 3.3 NewObject//实例化
*/
/* Get the method ID for the String constructor */
cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
if (cid == NULL) {
ret = -1;
printf("can not get constructor method");
goto destroy;
}
jobj = (*env)->NewObject(env, cls, cid);//在cid后面可以跟参数
if (jobj == NULL) {
ret = -1;
printf("can not create object");
goto destroy;
}
/* 4. call method
* 4.1 get method
* 4.2 create parameter
* 4.3 call method
*/
mid = (*env)->GetMethodID(env, cls, "sayhello_to","(Ljava/lang/String;)I");
if (mid == NULL) {
ret = -1;
printf("can not get method\n");
goto destroy;
}
jstr = (*env)->NewStringUTF(env, "weidongshan@qq.com");
r = (*env)->CallIntMethod(env, jobj, mid, jstr);//Call<Type>Method,Type是返回值类型
printf("ret = %d\n", r);
destroy:
(*jvm)->DestroyJavaVM(jvm);
return ret;
}
Hello.java
public class Hello {
public static void main(String args[]) {
System.out.println("Hello, world!");
}
public int sayhello_to(String name) {
System.out.println("Hello, "+name);
return 123;
}
public static void sayhello_to() {
}
}
javac Hello.java
javap -p -s Hello.class // get Signature
gcc -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o caller caller.c -L /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server -ljvm
LD_LIBRARY_PATH=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server ./caller
eg:设置和获取类的属性,参考pdf55页
#include <stdio.h>
#include <jni.h>
jint create_vm(JavaVM** jvm, JNIEnv** env)
{
JavaVMInitArgs args;
JavaVMOption options[1];
args.version = JNI_VERSION_1_6;
args.nOptions = 1;
options[0].optionString = "-Djava.class.path=./";
args.options = options;
args.ignoreUnrecognized = JNI_FALSE;
return JNI_CreateJavaVM(jvm, (void **)env, &args);
}
int main(int argc, char **argv)
{
JavaVM* jvm;
JNIEnv* env;
jclass cls;
int ret = 0;
jmethodID mid;
jmethodID cid;
jobject jobj;
jstring jstr;
jfieldID nameID;
jfieldID ageID;
int r;
/* 1. create java virtual machine */
if (create_vm(&jvm, &env)) {
printf("can not create jvm\n");
return -1;
}
/* 2. get class */
cls = (*env)->FindClass(env, "Hello");
if (cls == NULL) {
printf("can not find hello class\n");
ret = -1;
goto destroy;
}
/* 3. create object
* 3.1 get constructor method
* 3.2 create parameters
* 3.3 NewObject
*/
/* Get the method ID for the String constructor */
cid = (*env)->GetMethodID(env, cls, "<init>", "()V");
if (cid == NULL) {
ret = -1;
printf("can not get constructor method");
goto destroy;
}
jobj = (*env)->NewObject(env, cls, cid);
if (jobj == NULL) {
ret = -1;
printf("can not create object");
goto destroy;
}
/* get/set field
* 1. get field id
* 2. get/set field
*/
nameID = (*env)->GetFieldID(env, cls, "name", "Ljava/lang/String;");
if (nameID == NULL) {
ret = -1;
printf("can not get field name");
goto destroy;
}
jstr = (*env)->NewStringUTF(env, "Bill");
(*env)->SetObjectField(env, jobj, nameID, jstr);
ageID = (*env)->GetFieldID(env, cls, "age", "I");
if (ageID == NULL) {
ret = -1;
printf("can not get field age");
goto destroy;
}
(*env)->SetIntField(env, jobj, ageID, 10);
/* 4. call method
* 4.1 get method
* 4.2 create parameter
* 4.3 call method
*/
mid = (*env)->GetMethodID(env, cls, "sayhello_to","(Ljava/lang/String;)I");
if (mid == NULL) {
ret = -1;
printf("can not get method\n");
goto destroy;
}
jstr = (*env)->NewStringUTF(env, "weidongshan@qq.com");
r = (*env)->CallIntMethod(env, jobj, mid, jstr);
printf("ret = %d\n", r);
destroy:
(*jvm)->DestroyJavaVM(jvm);
return ret;
}
Hello.java
public class Hello {
private String name;
private int age;
public static void main(String args[]) {
System.out.println("Hello, world!");
}
public int sayhello_to(String name) {
System.out.println("Hello, "+name+"! I am "+this.name+", "+age+" years old.");
return 123;
}
public static void sayhello_to() {
}
}
javac Hello.java
javap -p -s Hello.class // get Signature
gcc -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -o caller caller.c -L /usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server -ljvm
LD_LIBRARY_PATH=/usr/lib/jvm/java-7-openjdk-amd64/jre/lib/amd64/server ./caller
2、JNI说明的更多相关文章
- android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测
目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. (原创:转载声明出处:http://www.cnblogs.com/lin ...
- 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案
相信很多小伙伴都在android开发中遇到调用jni的各种巨坑,因为我们不得不在很多地方用到第三方库so文件,然而第三方官方通常都只会给出ADT环境下的集成方式,而谷歌亲儿子android studi ...
- Android游戏开发实践(1)之NDK与JNI开发03
Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...
- JNI 备注
本文记录一个基础的JNI例子及过程中遇到的问题解决. 1.定义一个JAVA类如下: package jnidemo01; public class JniHello { public native v ...
- Android游戏开发实践(1)之NDK与JNI开发01
Android游戏开发实践(1)之NDK与JNI开发01 NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码&q ...
- Android游戏开发实践(1)之NDK与JNI开发02
Android游戏开发实践(1)之NDK与JNI开发02 承接上篇Android游戏开发实践(1)之NDK与JNI开发01分享完JNI的基础和简要开发流程之后,再来分享下在Android环境下的JNI ...
- 关于jni编译32位、64位动态库(Android.mk和Application.mk文件)
最近新项目需要编译64位的动态库,这里记录如何配置. 在jni目录下加入Android.mk和Application.mk文件. Application.mk APP_ABI := armeabi a ...
- android JNI 调用NDK方法
@import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...
- jni操作jobject
一. 注册JNI函数 1. 静态方法 一般使用javah进行编译,生成很长的文件名和函数名字,这个书写不方便,影响运行效率. 2. 动态注册 使用JNINativeMe ...
- JNI开发的常见错误
1. 写错了load的library java.lang.UnsatisfiedLinkError: Couldn't load hell0: findLibrary returned null 2. ...
随机推荐
- SAR(遥感、卫星) 图像常用数据集
Brazilian Coffee Scenes数据集较小,5MB左右: UC Merced Land Use Dataset(数据集规模较大,300MB+) MSTAR public targets ...
- HDU 3594 The trouble of Xiaoqian 混合背包问题
The trouble of Xiaoqian Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/ ...
- DG archive gap
什么是archive gap Archive Gap就是standby端日志应用的过程中丢失的一段范围的redo.典型的发生在standby端不能接收primary的redo信息或者接收后不能应用这些 ...
- hdu4336Card Collector 概率dp+状态压缩
//给n个卡片每次出现的概率,求全部卡片都出现的须要抽的次数的期望 //dp[i]表示在状态的情况下到全部的卡片都出现的期望 //dp[i] = 1 + p1*dp[i] + ${p2[j]*dp[i ...
- 74LS153 选择器 【数字电路】
74LS153 我用了八个不同频率的方波信号,用153当作信号选择器,控制环节的开关是4通道的选择器,00 01 10 11分别选择通道 0 1 2 3 以下是八选一的demo
- 制作U盘启动盘将Ubuntu 12.04升级为14.04的方法
1 介绍 在周六的下午,我决定想高速浏览一下书籍[1].看看这个关于Ubuntu的圣经到底在讲什么东东. 感觉讲的不错,当我看到介绍文件标记语言-TeX和LaTeX的时候,该书作者推荐在Ubuntu上 ...
- POJ 3243 Clever Y Extended-Baby-Step-Giant-Step
题目大意:给定A,B,C,求最小的非负整数x,使A^x==B(%C) 传说中的EXBSGS算法0.0 卡了一天没看懂 最后硬扒各大神犇的代码才略微弄懂点0.0 參考资料: http://quarter ...
- js---15,模拟数组的ecah方法
原型的另外一个作用就是扩展对象中的属性和方法的 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http:// ...
- Multiple CPUs,Multiple Cores、Hyper-Threading
CPU Basics: Multiple CPUs, Cores, and Hyper-Threading Explained 现在多数的家用电脑,仍然使用的是 Single CPU,Multiple ...
- C#打印日志的小技巧(转)
https://www.cnblogs.com/jqg-aliang/p/5234206.html 打印日志的函数 开发中输出日志必不可少,在C#中输出多个不同类型参数的时候,需要连接符累加输出,很是 ...