JAVA进阶day07JNI(java调用c)A部分

目前接触的JNI有java调用c和c调用java两类。其中java调用c又有隐式和显示两种映射关系。本笔记针对java调用c的显示映射。本着工程实际够用的原则,不够再回头来补充。
JAVA访问c库需要有三个步骤
1:加载C库
2:建立java函数名到c库函数名的映射关系
3:在java程序里调用函数
先贴代码,依照代码来做分析:

public class JNIDemo {
    static {        /* 1. load */
        System.loadLibrary("native"); /* libnative.so */
    }
    public native void hello();
    public static void main (String args[]) {
        JNIDemo d = new JNIDemo();      

        /* 2. map java hello <-->c c_hello */

        /* 3. call */
        d.hello();
    }
}

分条缕析做笔记:

  • 1:加载C库和在java程序里调用函数
static {        /* 1. load */
        System.loadLibrary("native"); /* libnative.so */
    }

代码中在静态代码块中进行加载c库。可以知道我们要加载的是libnative.so库。放到静态代码块中可以保证该加载能够只执行一次。非常棒的处理。

public native void hello();

native修饰的hello表明这个hello方法不是在java中实现的而是在本地语言中实现的。我们在使用hello函数的时候首先要进行声明。这个声明也可以声明为静态的,比如public native static void hello();这时候就没有必要先实现JNIDemo的实例化静态对象了,也就是说我们可以省掉JNIDemo d=new JNI Demo();这个处理,直接使用JNIDemo.hello();当然我们的main函数也是在JNIDemo类中的,可以直接写为hello()。问题又来了,那么同样的道理为什么不是静态的还需要实现JNIDemo对象呢,这个也是在JNIDemo的main函数中啊?这个就没必要较真了,记住就好,又不是让你去创造java语言,你只不过是用这个工具而已。我还发现这个public native static void hello()跟public static native void hello()是一个效果。无碍乎这个static跟native的前后。到此我们对这个加载c库就算是掌握了。

  • 2:建立java函数名到c库函数名的映射关系
    为了将java中的hello跟c语言中的函数对应起来,这部分的处理是c语言中做的。我们姑且猜测,c语言在什么时候去做这个映射对应呢?我提出一个想法,我们在加载SystemloadLibrary的时候会引起c库文件中的一个触发,在那个触发函数里面做了对应。那么我们从文件中的结构应该是包含 触发 实现。我们贴代码分析:

#include <jni.h>  /* /usr/lib/jvm/java-1.7.0-openjdk-amd64/include/ */
#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)
{
    JNIEnv *env;
    jclass cls;

    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, methods, 1) < 0)
        return JNI_ERR;

    return JNI_VERSION_1_4;
}

我们看到,果不其然跟我们猜测的一模一样。
的确是分为两部分。最顶层那部分实现了c_hello函数,最下半部分实现了一个JNI_OnLoad触发函数。哇咔咔,一切都这么顺滑。那我们开垦这个触发函数吧。
该函数功能比较单一所以写法肯定是固定的。每次使用我们只需要去修改这么几个点就可以了:
JNI_VERSION_1_4 这个代表的是你使用的jni的版本。

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

这个作用就是从env找到JNIDemo这个类,方面后面我们去做映射。故而这个JNIDemo我们做移植的时候是需要去修改的。

    if ((*env)->RegisterNatives(env, cls, methods, 1) < 0)

这个地方也就是我们想要的注册了。后面的1代表你要注册的个数。
我们再看看这个注册methods里面的实现。

static const JNINativeMethod methods[] = {
    {"hello", "()V", (void *)c_hello},
};

这个模式也是固定的
其中“hello”对应的是java中的函数名字。c_hello对应的c文件中的函数名字。前面的void * 是永远不变的。中间的这个类型signature我是打死也不想记住怎么写的。我们可以在写完java文件之后使用javac JNIDemo.java进行编译然后使用
javah -jni JNIDemo
生成一个JNIDemo.h文件

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class JNIDemo */

#ifndef _Included_JNIDemo
#define _Included_JNIDemo
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JNIDemo
 * Method:    hello
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_JNIDemo_hello
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

我们从里面摘取

/*
 * Class:     JNIDemo
 * Method:    hello
 * Signature: ()V
 */

就知道这个signature 就是()V.
同时我们知道我们的c文件的hello函数书写形式应该类似与

JNIEXPORT void JNICALL Java_JNIDemo_hello
  (JNIEnv *, jclass);

实际中我们的c语言函数书写是

void c_hello(JNIEnv *env, jobject cls)

几乎是一样的,其中jobject 跟jclass我觉得应该是java编译器不同导致的,我感性的认为应该是兼容的,都能用。既然我的编译器出来的是jclass那么我的代码就修改为

void c_hello(JNIEnv *env, jclass cls)

写到这里,还剩下两件事,怎样编译c文件为lib*.so以及java同c语言文件如何传递返回数据类型。我们知道java跟c的数据类型上是有区别的。包括开辟的空间大小等。最起码的java中有string类型,然而我们的c语言中就没有。因此,这部分还算是一个不大不小的知识点。按下不表下集再说。

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值