scala和java通过JNI调用C++程序例子
2015-03-26 10:34
771 查看
转载:http://hohonuuli.blogspot.com/2013/08/a-simple-java-native-interface-jni.html 主要是国内访问不了这个网站
First off, let me be perfectly honest, if you have any choice besides using JNI, do that other thing. Dealing with native libraries has been my number
one source of pain when supporting Java applications. Now with that warning out of the way, sometimes you just can't avoid using JNI. For those who must go there, I hope this example helps and I salute your bravery and fortitude.
The Java and Scala examples do exactly the same thing. However, the byte code produced is slightly different since Scala doesn't have static methods.
Step 1 – Create a Java or Scala class
Write a Java or Scala class that will interface to the native library:
Java
In Java, methods that will call the native code will use the native keyword
in their method signature and have no method body. Create a file named Sample1.java and
add the code below:
Scala
The above code can be compiled using: Scala
Compiling Sample1.java will produce a single class named, Sample1.class
Scala
In Scala, methods that call native code will use the native annotation
and, like Java, have no method signature. Create a file named Sample1.scala and
add the code below.
Scala
Sample1.scala can be compiled using: Scala
When you compile Sample1.scala you will get three classes: Sample1.class, Sample1$.class, and Sample1$delayedInit$body.class.
Step 2 – Generate a header file with javah
The next step is to have the JVM produce a header file that defines the function prototypes needed for the native implementation.
Java
Creating the native header is really simple for Java. You just need the compiled class, Simple1.class, created in step1. Use the javah program
like so:
Scala
Notice that you do not use the .class extension. javah will produce a file named Sample1.h. Scala
Creating a native header from Scala code is slightly more involved in that you may need the scala libraries on the javahclasspath. In our example, since we are using Scala's App class we definitly need the scala libraries. In this example, we only need scala-library.jar and scala-reflect.jar. I'm working on a Mac and installed Scala via homebrew and my classpath below reflects that. Your path will likely be different depending on your platform. Scala
Output from javah will be Sample1.h
The Sample1.h file
produced here using Scala looks exactly the same as the file produced using the Java class. Just for grins, this is the output of javah:
Scala
Don't you just love C/C++? Step 3 – Create the native implementation
For both our Scala and Java examples, the native implementation is exactly the same. You can write implementations in either C or C++, although the code will be different. The sample below implements all the methods defined in Sample1.h in C++. Copy and paste the code below into a file named Sample1.cpp: Scala
Now the fun part, getting this to compile on your particular operating system. Since I'm working on a Mac here's the command line blurb I used to compile
Sample1.cpp to a shared native library:
Scala
A few notes about this:Notice the -dynamiclib flag, this creates a shared library and is required. On linux use -shared instead.
You have to include Java's jni headers which are always in at least 2 places. $JAVA_HOME/include
$JAVA_HOME/include/ . On Macs that's $JAVA_HOME/include/darwin, on Linux it's $JAVA_HOME/include/linux. I have no idea what it is on Windows.
You may have to set the JAVA_HOME variable on your machine. You can do this on a Mac using: export JAVA_HOME=
Remember the line in our Java/Scala code that goes: System.loadLibrary(“Sample1″). Although the name of the native library is referenced using “Sample1″ in our code, it's actually looked up using a 'lib' prefix. So the native library is actually named libSample1.dylib on Mac or libSample1.so on Linux.
If you're stuck developing for Java 6 on a Mac, change the native libraries extension to .jnilib or Java 6 won't find it. Java 7 on Mac requires .dylib extension. (I did try to warn you that JNI will test your strength)
Step 4 – Running the Code
Running either sample is straight-forward. However, you need to ensure that native library is on Java's or Scala's library path. The simplest way to do this is to add it to the java.library.path when you start your JVM. Java
Scala
Scala
Scala
or Scala
A Note about Deployment
It's not always feasible to set the java.library.path variable (e.g. when using Java Web Start). However, it is not particularly difficult to hack the path used internally by Java to add shared libraries at runtime. This is, however, a topic beyond the scope
of this posting.
First off, let me be perfectly honest, if you have any choice besides using JNI, do that other thing. Dealing with native libraries has been my number
one source of pain when supporting Java applications. Now with that warning out of the way, sometimes you just can't avoid using JNI. For those who must go there, I hope this example helps and I salute your bravery and fortitude.
The Java and Scala examples do exactly the same thing. However, the byte code produced is slightly different since Scala doesn't have static methods.
Step 1 – Create a Java or Scala class
Write a Java or Scala class that will interface to the native library:Java
In Java, methods that will call the native code will use the native keywordin their method signature and have no method body. Create a file named Sample1.java and
add the code below:
Scala
123456789101112131415161718192021222324 | class Sample1 { // --- Native methods public native int intMethod(int n); public native boolean booleanMethod(boolean bool); public native String stringMethod(String text); public native int intArrayMethod(int[] intArray); // --- Main method to test our native library public static void main(String[] args) { System.loadLibrary("Sample1"); Sample1 sample = new Sample1(); int square = sample.intMethod(5); boolean bool = sample.booleanMethod(true); String text = sample.stringMethod("java"); int sum = sample.intArrayMethod(new int[] {1, 1, 2, 3, 5, 8, 13}); System.out.println("intMethod: " + square); System.out.println("booleanMethod: " + bool); System.out.println("stringMethod: " + text); System.out.println("intArrayMethod: " + sum); }} |
1 | javac Sample1.java |
Scala
In Scala, methods that call native code will use the native annotationand, like Java, have no method signature. Create a file named Sample1.scala and
add the code below.
Scala
123456789101112131415161718192021222324 | class Sample1 { // --- Native methods @native def intMethod(n: Int): Int @native def booleanMethod(b: Boolean): Boolean @native def stringMethod(s: String): String @native def intArrayMethod(a: Array[Int]): Int} // --- Code in App body will get wrapped in a main method on compilationobject Sample1 extends App { // --- Main method to test our native library System.loadLibrary("Sample1") val sample = new Sample1 val square = sample.intMethod(5) val bool = sample.booleanMethod(true) val text = sample.stringMethod("java") val sum = sample.intArrayMethod(Array(1, 1, 2, 3, 5, 8, 13)) println(s"intMethod: $square") println(s"booleanMethod: $bool") println(s"stringMethod: $text") println(s"intArrayMethod: $sum")} |
1 | scalac Sample1.scala |
Step 2 – Generate a header file with javah
The next step is to have the JVM produce a header file that defines the function prototypes needed for the native implementation.Java
Creating the native header is really simple for Java. You just need the compiled class, Simple1.class, created in step1. Use the javah programlike so:
Scala
1 | javah Sample1 |
Scala
Creating a native header from Scala code is slightly more involved in that you may need the scala libraries on the javahclasspath. In our example, since we are using Scala's App class we definitly need the scala libraries. In this example, we only need scala-library.jar and scala-reflect.jar. I'm working on a Mac and installed Scala via homebrew and my classpath below reflects that. Your path will likely be different depending on your platform. Scala1 2 3 | SCALA_LIB_HOME=/usr/local/Cellar/scala/2.10.2/libexec/lib/ SCALA_CP=$SCALA_LIB_HOME/scala-library.jar:$SCALA_LIB_HOME/scala-reflect.jar javah -cp $SCALA_CP:. Sample1 |
Output from javah will be Sample1.h
The Sample1.h fileproduced here using Scala looks exactly the same as the file produced using the Java class. Just for grins, this is the output of javah:
Scala
12345678910111213141516171819202122232425262728293031323334353637383940414243444546 | /* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class Sample1 */ #ifndef _Included_Sample1#define _Included_Sample1#ifdef __cplusplusextern "C" {#endif/* * Class: Sample1 * Method: intMethod * Signature: (I)I */JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv *, jobject, jint); /* * Class: Sample1 * Method: booleanMethod * Signature: (Z)Z */JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod (JNIEnv *, jobject, jboolean); /* * Class: Sample1 * Method: stringMethod * Signature: (Ljava/lang/String;)Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_Sample1_stringMethod (JNIEnv *, jobject, jstring); /* * Class: Sample1 * Method: intArrayMethod * Signature: ([I)I */JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod (JNIEnv *, jobject, jintArray); #ifdef __cplusplus}#endif#endif</jni.h> |
Step 3 – Create the native implementation
For both our Scala and Java examples, the native implementation is exactly the same. You can write implementations in either C or C++, although the code will be different. The sample below implements all the methods defined in Sample1.h in C++. Copy and paste the code below into a file named Sample1.cpp: Scala1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #include "Sample1.h" #include <ctype.h> #include <string.h> // Mutate array to uppercase void uppercase(char* str) { size_t n = strlen(str); for (size_t i = 0; i < n; i++) { str[i] = toupper(str[i]); } } JNIEXPORT jint JNICALL Java_Sample1_intMethod (JNIEnv* env, jobject obj, jint num) { return num * num; } JNIEXPORT jboolean JNICALL Java_Sample1_booleanMethod (JNIEnv* env, jobject obj, jboolean boolean) { return !boolean; } JNIEXPORT jstring JNICALL Java_Sample1_stringMethod (JNIEnv* env, jobject obj, jstring string) { const char* str = env->GetStringUTFChars(string, 0); char cap[128]; strcpy(cap, str); env->ReleaseStringUTFChars(string, str); uppercase(cap); return env->NewStringUTF(cap); } JNIEXPORT jint JNICALL Java_Sample1_intArrayMethod (JNIEnv* env, jobject obj, jintArray array) { int sum = 0; jsize len = env->GetArrayLength(array); jint* body = env->GetIntArrayElements(array, 0); for (int i = 0; i < len; i++) { sum += body[i]; } env->ReleaseIntArrayElements(array, body, 0); return sum; } </string.h></ctype.h> |
Sample1.cpp to a shared native library:
Scala
123 | g++ -dynamiclib -G -Wall -O3 \ -I/usr/include -I$JAVA_HOME/include -I$JAVA_HOME/include/darwin \ Sample1.cpp -o libSample1.dylib |
You have to include Java's jni headers which are always in at least 2 places. $JAVA_HOME/include
$JAVA_HOME/include/ . On Macs that's $JAVA_HOME/include/darwin, on Linux it's $JAVA_HOME/include/linux. I have no idea what it is on Windows.
You may have to set the JAVA_HOME variable on your machine. You can do this on a Mac using: export JAVA_HOME=
/usr/libexec/java_home -v 1.7
Remember the line in our Java/Scala code that goes: System.loadLibrary(“Sample1″). Although the name of the native library is referenced using “Sample1″ in our code, it's actually looked up using a 'lib' prefix. So the native library is actually named libSample1.dylib on Mac or libSample1.so on Linux.
If you're stuck developing for Java 6 on a Mac, change the native libraries extension to .jnilib or Java 6 won't find it. Java 7 on Mac requires .dylib extension. (I did try to warn you that JNI will test your strength)
Step 4 – Running the Code
Running either sample is straight-forward. However, you need to ensure that native library is on Java's or Scala's library path. The simplest way to do this is to add it to the java.library.path when you start your JVM. Java
Scala1 2 3 | # Assumes all files are in the same directory. # If they are not, replace $(pwd) with the directory containing libSample1.so or libSample1.dylib java -Djava.library.path=$(pwd) -cp . Sample1 |
Scala
Scala1 | scala -Djava.library.path=$(pwd) -cp . Sample1 |
1 2 3 | SCALA_LIB_HOME=/usr/local/Cellar/scala/2.10.2/libexec/lib/ SCALA_CP=$SCALA_LIB_HOME/scala-library.jar:$SCALA_LIB_HOME/scala-reflect.jar java -Djava.library.path=$(pwd) -cp $SCALA_CP:. Sample1 |
A Note about Deployment
It's not always feasible to set the java.library.path variable (e.g. when using Java Web Start). However, it is not particularly difficult to hack the path used internally by Java to add shared libraries at runtime. This is, however, a topic beyond the scopeof this posting.
相关文章推荐
- 1Java程序通过JNI调用本地C++_第一个示例_HelloWorld
- Java通过JNI调用C++程序
- Java通过JNI调用C++程序
- Java通过JNI调用C++程序
- init.rc文件里面启动c++程序,通过jni调用java实现
- Java通过JNI调用C++程序
- Java通过JNI调用C++程序
- Android开发 通过JNI实现JAVA与C/C++程序间的调用和回调
- Java通过JNI调用C++程序
- Java通过JNI调用C++程序
- Java通过JNI调用C++程序
- JAVA程序通过JNI调用C/C++库
- Java通过JNI调用C++程序
- Java通过JNI调用本地C/C++程序
- Java通过JNI调用本地C/C++程序--常用示例
- JAVA通过JNI调用C++程序实践
- 在eclipse下,JAVA通过JNI调用C++方法的简单例子(非android)
- AndroidJNI 通过C++调用JAVA
- Mac环境Java通过jni调用C++ OpenCv代码
- Linux环境Java通过jni调用C++ OpenCv代码