• 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! ");

    }    

    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 ",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 ");
      return -1;

      }

      /*2、get class*/

      cls = (*env)->FindClass(env,"Hello");

      if (cls == NULL) {
      printf("can not find hello class ");
      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 ");
      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 ");
        return -1;
      }

      /* 2. get class */
      cls = (*env)->FindClass(env, "Hello");
      if (cls == NULL) {
        printf("can not find hello class ");
        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 ");

        goto destroy;
      }

      jstr = (*env)->NewStringUTF(env, "weidongshan@qq.com");

      r = (*env)->CallIntMethod(env, jobj, mid, jstr);//Call<Type>Method,Type是返回值类型
      printf("ret = %d ", 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 ");
        return -1;
      }

      /* 2. get class */
      cls = (*env)->FindClass(env, "Hello");
      if (cls == NULL) {
        printf("can not find hello class ");
        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 ");
    goto destroy;
    }

    jstr = (*env)->NewStringUTF(env, "weidongshan@qq.com");

    r = (*env)->CallIntMethod(env, jobj, mid, jstr);
    printf("ret = %d ", 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

  • 相关阅读:
    【题解】字母 (letter)
    【题解】【原创题目】薇尔莉特
    【题解】Query on a tree III [SP1487] [Bzoj1803]
    11个炫酷的 Linux 终端命令
    A light-weight client-side OAuth library for Java
    android下的异步任务
    OAuth相关知识
    显示单位px、dip以及sp的区别
    Arduino连接SHT10温湿度传感器--返回值不正常解决办法
    blueterm蓝牙超级终端(源码)
  • 原文地址:https://www.cnblogs.com/liusiluandzhangkun/p/9105157.html
Copyright © 2020-2023  润新知