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说明的更多相关文章

  1. android studio 使用 jni 编译 opencv 完整实例 之 图像边缘检测!从此在andrid中自由使用 图像匹配、识别、检测

    目录: 1,过程感慨: 2,运行环境: 3,准备工作: 4,编译 .so 5,遇到的关键问题及其解决方法 6,实现效果截图. (原创:转载声明出处:http://www.cnblogs.com/lin ...

  2. 【走过巨坑】android studio对于jni调用及运行闪退无法加载库的问题解决方案

    相信很多小伙伴都在android开发中遇到调用jni的各种巨坑,因为我们不得不在很多地方用到第三方库so文件,然而第三方官方通常都只会给出ADT环境下的集成方式,而谷歌亲儿子android studi ...

  3. Android游戏开发实践(1)之NDK与JNI开发03

    Android游戏开发实践(1)之NDK与JNI开发03 前面已经分享了两篇有关Android平台NDK与JNI开发相关的内容.以下列举前面两篇的链接地址,感兴趣的可以再回顾下.那么,这篇继续这个小专 ...

  4. JNI 备注

    本文记录一个基础的JNI例子及过程中遇到的问题解决. 1.定义一个JAVA类如下: package jnidemo01; public class JniHello { public native v ...

  5. Android游戏开发实践(1)之NDK与JNI开发01

    Android游戏开发实践(1)之NDK与JNI开发01 NDK是Native Developement Kit的缩写,顾名思义,NDK是Google提供的一套原生Java代码与本地C/C++代码&q ...

  6. Android游戏开发实践(1)之NDK与JNI开发02

    Android游戏开发实践(1)之NDK与JNI开发02 承接上篇Android游戏开发实践(1)之NDK与JNI开发01分享完JNI的基础和简要开发流程之后,再来分享下在Android环境下的JNI ...

  7. 关于jni编译32位、64位动态库(Android.mk和Application.mk文件)

    最近新项目需要编译64位的动态库,这里记录如何配置. 在jni目录下加入Android.mk和Application.mk文件. Application.mk APP_ABI := armeabi a ...

  8. android JNI 调用NDK方法

    @import url(http://i.cnblogs.com/Load.ashx?type=style&file=SyntaxHighlighter.css);@import url(/c ...

  9. jni操作jobject

    一. 注册JNI函数 1.         静态方法 一般使用javah进行编译,生成很长的文件名和函数名字,这个书写不方便,影响运行效率. 2.         动态注册 使用JNINativeMe ...

  10. JNI开发的常见错误

    1. 写错了load的library java.lang.UnsatisfiedLinkError: Couldn't load hell0: findLibrary returned null 2. ...

随机推荐

  1. Yahoo!团队:网站性能优化的35条黄金守则(转)

    Excetional Performance 团队总结出了一系列可以提高网站速度的方法.可以分为 7大类 35条.包括内容 .服务器 . CSS . JavaScript .Cookie .图片 .移 ...

  2. 虚拟局域网(VLAN)技术在企业网管理中的应用

    虚拟局域网(VLAN)技术在企业网管理中的应用 1.VLAN介绍     所谓VLAN 是指处于不同物理位置的节点根据需要组成不同的逻辑子网,即一个VLAN 就是一个逻辑广播域,它可以覆盖多个网络设备 ...

  3. Kinect 开发 —— 常见手势识别(下)

    划动(Swipe) 划动手势和挥手(wave)手势类似.识别划动手势需要不断的跟踪用户手部运动,并保持当前手的位置之前的手的位置.因为手势有一个速度阈值,我们需要追踪手运动的时间以及在三维空间中的坐标 ...

  4. Intellij IDEA 部署Web项目,解决 404 错误

    https://blog.csdn.net/eaphyy/article/details/72513914

  5. logrotate---日志分割

    logrotate命令用于对系统日志进行轮转.压缩和删除,也可以将日志发送到指定邮箱.使用logrotate指令,可让你轻松管理系统所产生的记录文件.每个记录文件都可被设置成每日,每周或每月处理,也能 ...

  6. XML学习总结(1)——XML入门

    一.XML语法学习 学习XML语法的目的就是编写XML 一个XML文件分为如下几部分内容: 文档声明 元素 属性 注释 CDATA区 .特殊字符 处理指令(processing instruction ...

  7. Prism 框架基础架构

    概要 Prism提供指导,帮助您更轻松地设计和构建,灵活且易于维护的客户端业务应用程序,这些应用程序可在Windows运行时,Windows Presentation Foundation(WPF)桌 ...

  8. ArcGIS “Error HRESULT E_FAIL has been returned from a call to a COM component.” 异常的解决

    错误提示内容: {System.Runtime.InteropServices.COMException (0x80004005): Error HRESULT E_FAIL has been ret ...

  9. linux 配置IP地址

    linux 配置IP网址能够使用neat,netconfig,ifconfig等进行配置,当中前两个实用户界面,第三个命令仅仅是暂时设置IP,机器重新启动后配置将丢失. 有时候图形用户界面的程序难以获 ...

  10. PHP 版本简单记录

    PHP 版本简单记录 PHP 博物馆         http://museum.php.net/php5/ PHP 版本发布       https://secure.php.net/release ...