首页 > 代码库 > JAVA调用C语言写的SO文件

JAVA调用C语言写的SO文件

  JAVA调用C语言写的SO文件

  因为工作需要写一份SO文件,作为手机硬件IC读卡和APK交互的桥梁,也就是中间件,看了网上有说到JNI接口技术实现,这里转载了一个实例

 1 // 用JNI实现 2 // 实例: 3  4 // 创建HelloWorld.java 5 class HelloWorld 6 { 7     private native void print(); 8     public static void main(String[] args) 9     {10         new HelloWorld().print();11     }12 13     static14     {15         System.loadLibrary("HelloWorld");16     }17 }18 // 注意print方法的声明,关键字native表明该方法是一个原生代码实现的。另外注意static代码段的System.loadLibrary调用,这段代码表示在程序加载的时候,自动加载libHelloWorld.so库。19 // 编译HelloWorld.java20 // 在命令行中运行如下命令:21 javac HelloWorld.java22 // 在当前文件夹编译生成HelloWorld.class。23 // 生成HelloWorld.h24 // 在命令行中运行如下命令:25 javah -jni HelloWorld26 // 在当前文件夹中会生成HelloWorld.h。打开HelloWorld.h将会发现如下代码:27 /* DO NOT EDIT THIS FILE - it is machine generated */28 #include <jni.h>29 /* Header for class HelloWorld */30 31 #ifndef _Included_HelloWorld32 #define _Included_HelloWorld33 #ifdef __cplusplus34 extern "C" {35 #endif36 /*37  * Class:     HelloWorld38  * Method:    print39  * Signature: ()V40  */41 JNIEXPORT void JNICALL Java_HelloWorld_print42 (JNIEnv *, jobject);43 44 #ifdef __cplusplus45 }46 #endif47 #endif48 // 该文件中包含了一个函数Java_HelloWorld_print的声明。这里面包含两个参数,非常重要,后面讲实现的时候会讲到。49 // 实现HelloWorld.c50 // 创建HelloWorld.c文件输入如下的代码:51 #include <jni.h>52 #include <stdio.h>53 #include "HelloWorld.h"54 55 JNIEXPORT void JNICALL56 Java_HelloWorld_print(JNIEnv *env, jobject obj)57 {58     printf("Hello World!\n");59 }60 // 注意必须要包含jni.h头文件,该文件中定义了JNI用到的各种类型,宏定义等。61 // 另外需要注意Java_HelloWorld_print的两个参数,本例比较简单,不需要用到这两个参数。但是这两个参数在JNI中非常重要。62 // env代表java虚拟机环境,Java传过来的参数和c有很大的不同,需要调用JVM提供的接口来转换成C类型的,就是通过调用env方法来完成转换的。63 // obj代表调用的对象,相当于c++的this。当c函数需要改变调用对象成员变量时,可以通过操作这个对象来完成。64 // 编译生成libHelloWorld.so65 // 在Linux下执行如下命令来完成编译工作:66 cc -I/usr/lib/jvm/java-6-sun/include/linux/67 -I/usr/lib/jvm/java-6-sun/include/68 -fPIC -shared -o libHelloWorld.so HelloWorld.c69 // 在当前目录生成libHelloWorld.so。注意一定需要包含Java的include目录(请根据自己系统环境设定),因为Helloworld.c中包含了jni.h。70 // 另外一个值得注意的是在HelloWorld.java中我们LoadLibrary方法加载的是“HelloWorld”,可我们生成的Library却是libHelloWorld。这是Linux的链接规定的,一个库的必须要是:lib+库名+.so。链接的时候只需要提供库名就可以了。71 // 运行Java程序HelloWorld72 // 大功告成最后一步,验证前面的成果的时刻到了:73 java HelloWorld74 // 如果你这步发生问题,如果这步你收到java.lang.UnsatisfiedLinkError异常,可以通过如下方式指明共享库的路径:75 java -Djava.library.path=‘.‘ HelloWorld76 // 当然还有其他的方式可以指明路径请参考《在Linux平台下使用JNI》。77 // 我们可以看到久违的“Hello world!”输出了。
View Code

  试着去完成,自己生成了一份com_test_GetMsg.h头文件,并完成test.c,生成libtest.so文件,JAVA调用SO文件时,屡次报:

failed: Cannot load library: load_library(linker.cpp:761): not a valid ELF executable: /data/app-lib/com.example.iccommtest-libtest.so

  也就是提供的SO无法load,是valid的。

  后来比对刚才的实例,发现问题既然是com_test_GetMsg.h的文件名,因为com_test是类名,而GetMsg是方法名,但是方法名的命名各种是这样: 

1 1 /*2 2  * Class:     com_test3 3  * Method:    GetMsg4 4  * Signature: ()V5 5  */6 6 JNIEXPORT jstring JNICALL Java_com_test_GetMsg7 7 (JNIEnv *, jobject);
View Code

  这里问题来了,方法名的‘_‘是特殊符号,这样java在load编写的SO文件时,以为com是类名,test是方法名,所以一直load失败。好吧,是粗心惹的祸!

  重新生成test.h文件,方法名这样:

1 1 /*2 2  * Class:     test3 3  * Method:    GetMsg4 4  * Signature: ()V5 5  */6 6 JNIEXPORT jstring JNICALL Java_test_GetMsg7 7 (JNIEnv *, jobject);
View Code

  这样问题解决了!

  我非专业JAVA开发的,写的有点不顺,欢迎JAVA牛人吐槽

JAVA调用C语言写的SO文件