zoukankan      html  css  js  c++  java
  • JNI学习笔记_Java调用C —— Android中使用的方法

    一、笔记

    1.JNI(Java Native Interface),就是如何使用java去访问C/C++编写的那些库。
    若想深入了解JNI可以看官方文档jni.pdf
    优秀博文:
    Android JNI知识简介:http://blog.csdn.net/linweig/article/details/5417319
    Android JNI(实现自己的JNI_OnLoad函数):http://jjf19850615.blog.163.com/blog/static/356881472013342153912 (这个看不了了)


    2.JNI协议作用位置

    Java Android是通过Java写的
    ---------------
    JNI JNI是Java调用C/C++库的协议
    ---------------
    C/C++ App、C库 可以直接系统调用
    ---------------
    驱动 Linux


    3.JNI的实现步骤:
    (1) 加载C库
    (2) 建立java函数与C函数的映射,有两种建立方法,分别为显式建立和隐式建立
      (1)显式建立:使用(*env)->RegisterNatives()注册。
      (2)隐式建立:如果java源文件中类a.b.c.d.JNIDemo要调用hello(),那么C源文件中要实现Java_a_b_c_d_JNIDemo_hello()这个函数,
        函数名要严格对应。对应的C中的函数的函数名同样可以使用javah来生成。
        使用隐式建立时C源文件中就不需要再定义JNI_OnLoad()了。
        但是一般倾向于建立显式建立,因为比较灵活。
    (3) 调用C函数

    4.jni.h文件
      可以通过它来看env中提供的函数的函数签名,其路径:/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/jni.h
      注意:需要使用-I指定jni.h的路径名以便#include可以包含的到它。

    5.JNI的字段描述符
    用于描述C源文件中的JNINativeMethod.signature字段
      ①参数中的数组使用[表示,例如int[] a为[I
      ②对于类要用全称“L包.子包.类名;”(前面有L后面有;),eg:"Ljava/lang/String;"
      除了String类之外的其它所有类都要使用“L/java/lang/Object”来表示。
      对于类参数和返回值都是一样的表示。

    也可以使用一个取巧的办法,使用javah生成一个JNIDemo.h头文件,这个头文件中就有这些描述符。
    $ javac JNIDemo.java
    $ javah -jni JNIDemo

    6.JNI传递参数
    (1) 基本类型的参数传递非常简单,java传给C文件的参数C文件中直接使用就可以了。对基本类型的参数传递是透明的。
    (2) 参数或返回值是类名时对应C中的函数签名可以借助javah来查看
      ①对于字符串类的双向传递需要使用env中的一些函数。参考jni.pdf pg39,
      ②传递数组时C文件中的函数签名可以参考javah生成的头文件。参考jni.pdf pg48

    7.java函数映射的C文件中的函数前面的修饰JNICALL是一个空的宏,写不写无所谓。

    8.java文件怎样使用native函数总结
    java文件中声明native方法,然后在类的静态代码块中调用System.loadLibrary("本地库")来加载本地库,这会触发本地库中的onLoad()被调用。
    本地需要实现java类中的native方法,并在onLoad()中将这些方法注册到java类中。此后java就可以调用本地方法了。

    二、Java调用C函数的例子

    1.基本实现框架

    /*file: JNIDemo. java*/

    public
    class JNIDemo { /* * 一般来说会在静态代码块里来加载这个库。因为一 * 个静态代码块会在构造一个对象之前执行,并且只 * 执行一次。 * 它会导致C中实现的JNI_OnLoad()被调用。 */ static { /* 1. load */ System.loadLibrary("native"); /* 加载libnative.so这个C库 */ } /* * 加native关键字修饰表示hello()是定义在C文件中的, * 这里只是简单的修饰就可以调用了。注意:hello()前 * 没有加static,因此需要创建一个实例化对象调用hello() */ public native int hello(int a); public static void main (String args[]) { JNIDemo d = new JNIDemo(); /*创建JNIDemo类的对象d*/ /* 2. map java hello <-->c c_hello */ /*hello()是在java中实现的,c_hello()是在C中实现的,映射关系的建立是在C中建立的(显式映射)*/ /* 3. call */ System.out.println(d.hello()); } } /* 非static 函数需要通过对象来调用,static可以直接调用*/
    public class JNIDemo { static { /* 1. load */ System.loadLibrary("native"); /* 加载libnative.so这个C库 */ } public native static void hello(); /*加native关键字修饰表示hello()是定义在C文件中的,这里知识简单的修饰就可以调用了*/ public static void main (String args[]) {/* 2. map java hello <-->c c_hello */ /* 3. call */ hello(); /*与上一个不同,上面申明hello()时加了static修饰,所以这里可以直接调用*/ } }
    /*file: native.c*/

    /*
    * jni.h位于/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/目录下, * --sysroot指定这个路径,因此编译C程序时需要指定: * -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */ #include <jni.h> #include <stdio.h> #if 0 typedef struct { char *name; /* 这个name就是在Java里调用的函数名,此列子为hello */ char *signature; /* JNI字段描述符, 用来表示Java里调用的函数的参数和返回值类型 */ void *fnPtr; /* C语言实现的本地函数的函数指针 */ } JNINativeMethod; #endif

      jint c_hello(JNIEnv *env, jobject obj, jint a) {
        printf("Hello JNI, a=%d ", a);
       return 222;
      }

    /*
     * 既然methods[]是个数组就说明可以同时注册多个本地函数
     */
    static const JNINativeMethod methods[] = {
        /*
         * 本地定义的这个c_hello(除了env和cls)没有参数,
         * 所以signature字段中‘()’里面为空,返回值是void
         * 所以是"()V". 没有参数是指在java中调用这个函数没有参数。
         * 函数签名的写法补充:
         * 参数中的数组使用[表示,例如int[] a为[I
         * 对于类要用全称“L包.子包.类名;”(前面有L后面有;),eg:"Ljava/lang/String;"
         * 除了String类之外的其它所有类都要使用“L/java/lang/Object”来表示。
         * 对于类参数和返回值都是一样的表示。
         */
        {"hello", "(I)I", (void *)c_hello},
    };
    
    /* java程序中执行System.loadLibrary()加载这个C库的时候 * 就会先执行C库中定义的JNI_OnLoad(),因此需要在这个函数中 * 建立映射关系。 * 这个函数怎么写参考jni.pdf pg117 */ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) { JNIEnv *env; jclass cls; /* * 获取java程序的运行环境,因为之后需要通过env调用FindClass * 等函数。 * JNI有1.1 1.2 1.4这几个版本,最后还要返回这个版本,1.1是最 * 老的。 */ if ((*jvm)->GetEnv(jvm, (void **)&env, JNI_VERSION_1_4)) { /*获取运行环境*/ return JNI_ERR; /* JNI version not supported */ } /* * java中的函数是通过类来写的。C中的c_hello()要映射 * 到JNIDemo中声明的hello()上,所以要先查找这个类。 */ cls = (*env)->FindClass(env, "JNIDemo"); if (cls == NULL) { return JNI_ERR; } /* * 建立映射关系,cls是通过java程序获取到的,methods是 * C程序中的。 * methods数组中的数组项只有1项。 */ /* 2. map java hello <-->c c_hello */ if ((*env)->RegisterNatives(env, cls, methods, 1) < 0) return JNI_ERR; return JNI_VERSION_1_4; }

    上面是显式映射的例子,下面是隐式映射:

    /*file: native.c*/

    #include <stdio.h> #include <jni.h> JNIEXPORT jint JNICALL Java_JNIDemo_hello(JNIEnv *env, jobject obj, jint a) { printf("Hello JNI, a=%d ", a); return 222; }

    编译运行方法:

    javac JNIDemo.java
    gcc -I/usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ -fPIC -shared -o libnative.so native.c
    export LD_LIBRARY_PATH=.
    java JNIDemo 
    
    注意:
    1.编译成C库的操作中若没有加-fPIC会报错“Couldn't read symbols”.
    2.需要设置环境变量LD_LIBRARY_PATH,否则“no native in java.library.path”

    2.双向传递字符串

    JNIDemo.java中修改如下:

    public class JNIDemo {
    
        static { // 1. load
            System.loadLibrary("native");
         }
    
        public native String hello(String str); //still is int in java, but jint in C
    
        public static void main(String args[]) {
            JNIDemo d = new JNIDemo();
    
            System.out.println(d.hello("Hello JNI String!"));
        }
        
    }

    native.c中修改如下:

    jstring c_hello(JNIEnv *env, jobject cls, jstring str) {
        const jbyte *cstr;
        cstr = (*env)->GetStringUTFChars(env, str, NULL);
        printf("Get string from java :%s
    ", cstr);
        (*env)->ReleaseStringUTFChars(env, str, cstr);
        return (*env)->NewStringUTF(env, "return from c");
    }
    
    static const JNINativeMethod methods[] = {
        {"hello", "(Ljava/lang/String;)Ljava/lang/String;", (void *)c_hello},
    };

    3.双向传递数组

    /*file: JNIDemo.java*/
    
    public class JNIDemo {
    
        static { // 1. load
            System.loadLibrary("native");
         }
    
        public native int[] hello(int[] arr);
    
        public static void main(String args[]) {
            JNIDemo d = new JNIDemo();
    
            int[] arr = {1, 2, 3};
            int[] b = null;
            int i;
    
            b = d.hello(arr);
    
            for (i = 0; i < b.length; i++)
                System.out.println(b[i]);
        }
        
    }
    /*file: native.c*/
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <jni.h>
    
    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; /* exception occurred */
        }
    
        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;
    }
    
    static const JNINativeMethod methods[] = {
        {"hello", "([I)[I", (void *)c_hello},
    };
    
    
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
        JNIEnv *env;
        jclass cls;
    
        if ((*vm)->GetEnv(vm, (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, methods, 1) < 0)
            return JNI_ERR;
    
        return JNI_VERSION_1_4;
    }

    三、相关资源收集

    1. WDS JNIDemo源码:$ git clone https://github.com/weidongshan/JNIDemo.git

    四、补充

    1.$ java -verbose:jni StaticMethodCal 可以看出JNI库加载的信息

    2. 签名中使用的是具体的类名

    // frameworks/base/core/jni/android_hardware_SensorManager.cpp
    
    static JNINativeMethod gSystemSensorManagerMethods[] = {
        {"nativeClassInit", "()V", (void*)nativeClassInit },
        /*L+包名+类名*/
        {"nativeGetNextSensor", "(Landroid/hardware/Sensor;I)I", (void*)nativeGetNextSensor },
    };
    static JNINativeMethod gBaseEventQueueMethods[] = {
        {"nativeInitBaseEventQueue",
        /*类的内部类直接使用"$"连接*/
                "(Landroid/hardware/SystemSensorManager$BaseEventQueue;Landroid/os/MessageQueue;[F)J",
                (void*)nativeInitSensorEventQueue },
    };
  • 相关阅读:
    HDU
    POJ
    POJ
    POJ
    POJ
    POJ
    POJ
    SPFA算法——最短路径
    POJ1251 Jungle Roads Kruskal+scanf输入小技巧
    MongoDB--关于数据库及选择MongoDB的原因
  • 原文地址:https://www.cnblogs.com/hellokitty2/p/10390938.html
Copyright © 2011-2022 走看看