Again, the logic is pretty straightforward but the exception you might run into might take time to resolve it. Here is what's I try and I highlight them in yellow.
outline
- Creating the Java Class for JNI shared ( function / arguments )
- Compiling the Java Class for Generating C++ header
- Composing the C++ cpp or cc class
- Sorting out the "jni.h" location as g++ reference
- Compiling the C++ code for dynamic c lib
- Run Java to call C++ Class
- Using Eclipse in Ubuntu
- C++ Shared Library Project
- Java Project to run *.so
Creating the Java Class for JNI shared ( function / arguments )
We start by creating a Hello World java class with the C++ lib Class name we would like to have and method(function) we would like to use in C++ code.
$ vi HelloWorld.java
$ cat HelloWorld.java
class HelloWorld {
private native void print();
public static void main(String[] args) {
new HelloWorld().print();
}
static {
System.loadLibrary("HelloWorld");
}
}
This static section get executed first, which expects to load a JNI shared or dynamic library known as "HelloWorld".
PS: HelloWorld is C++ lib name, HelloWorld is C++ class name and print is the method(function) name.
Compiling the Java Class for Generating C++ header
We need to compile the java class involves opening up the terminal and issuing the command as below, which is using javac to generate the class file that is needed to generate the appropriate JNI C++ classes.
$ javac HelloWorld.java
Creating the JNI C++ header, we can run the cli as below. I cat the C++ header content as below as well.
$ javah -jni HelloWorld
$ cat HelloWorld.h
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloWorld */
#ifndef _Included_HelloWorld
#define _Included_HelloWorld
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: HelloWorld
* Method: print
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_HelloWorld_print
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
Composing the C++ cpp or cc class
C++ header is more like a window or declaration for your class and method(function) which let java know how to connect with C++ lib. After we got the C++ *.h, we need to compile C++ native code into dynamic library. (In OS X, jni shared/dymaic libraries have the extension .jnilib).
$ vi HelloWorld.cpp
$ cat HelloWorld.cpp
#include <jni.h>
#include <iostream>
#include "HelloWorld.h"
using namespace std;
JNIEXPORT void JNICALL
Java_HelloWorld_print(JNIEnv *, jobject){
cout << "Oh Johnny, how handsome you are!\n";
return;
}
Sorting out the "jni.h" location as g++ reference
In OS X, using finder to type jni.h and find the location for right click the file you found and display the info, then you will see the location for jni.h.
Compiling the C++ code for dynamic c lib
Generating Object File
To include the native code in the compilation of the java program, we need to compile native C++ code into dynamic library (OS X, jni shared/dynamic libraries have the extension .jnilib.) This is big different from the extensions on Windows and Solaris machine:, dll and so respectively.
We will need to leverage the jni.h to compile the C++ code into C++ *.o which is object file. Here, we include the -c option to generate the object file: HelloWorld.o from C++ code: HelloWorld.cpp, plus including jni.h path.
$ ls -lart
-rw-r--r-- 1 johnnywa staff 200 Sep 29 22:52 HelloWorld.java
-rw-r--r-- 1 johnnywa staff 377 Sep 29 22:53 HelloWorld.h
-rw-r--r-- 1 johnnywa staff 214 Sep 29 22:55 HelloWorld.cpp
-rw-r--r-- 1 johnnywa staff 442 Sep 30 11:57 HelloWorld.class
drwxr-xr-x 12 johnnywa staff 408 Sep 30 12:47 .
$ g++ "-I/System/Library/Frameworks/JavaVM.framework/Versions/A/Headers" -c HelloWorld.cpp
$ ls -lart
-rw-r--r-- 1 johnnywa staff 200 Sep 29 22:52 HelloWorld.java
-rw-r--r-- 1 johnnywa staff 377 Sep 29 22:53 HelloWorld.h
-rw-r--r-- 1 johnnywa staff 214 Sep 29 22:55 HelloWorld.cpp
-rw-r--r-- 1 johnnywa staff 442 Sep 30 11:57 HelloWorld.class
-rw-r--r-- 1 johnnywa staff 6504 Sep 30 12:47 HelloWorld.o
drwxr-xr-x 13 johnnywa staff 442 Sep 30 12:47 .
Generating dynamiclib
Finally, we need to use the -dynamiclib option to specify that the compiler shouldn't produce a standard executable, but should produce a library. The -o option is used to name the library with the appropriate extension: libhelloworld.jnilib. We also include the object file we just generate in previous step.
$ g++ -dynamiclib -o libhelloworld.jnilib HelloWorld.o
$ ls -lart
-rw-r--r-- 1 johnnywa staff 200 Sep 29 22:52 HelloWorld.java
-rw-r--r-- 1 johnnywa staff 377 Sep 29 22:53 HelloWorld.h
-rw-r--r-- 1 johnnywa staff 214 Sep 29 22:55 HelloWorld.cpp
-rw-r--r-- 1 johnnywa staff 442 Sep 30 11:57 HelloWorld.class
-rw-r--r-- 1 johnnywa staff 6504 Sep 30 12:47 HelloWorld.o
-rwxr-xr-x 1 johnnywa staff 14764 Sep 30 12:51 libhelloworld.jnilib
drwxr-xr-x 14 johnnywa staff 476 Sep 30 12:51 .
Run Java to call C++ Class
Since the java file and libhelloworld.jnilib is sitting in the same directory
$ java HelloWorld
Oh Johnny, how handsome you are!
Using Eclipse to in Linux ( Ubuntu )
C++ Shared Library Project for C++ object
Using in Eclipse is the same just create a C++ project but using "Shared Library" and Choose "Linux C++", the copy HelloWorld.h under src.
and Create a new Class file call "HelloWorld.cpp" under src as well, then put the same content like cli in OS X.
but remember we need to reference the jni.h path ?, here you add includes via right click project and select properties.
The reference path can be as below.
Double check , whether you "check" - "Shared"
Then you set up run configuration as below, especially double check your C++ Application: (Path).
Last, one of the key is select "-fPIC", this is the key when you compiler in Linux.
After you right click build the project, you should be able see there has new *.so shared library under debug folder.
PS: if you got the required C++11 error, you can add compiler parameter as below.
Java Project reference C++ *.so
Then, back to your Java project, if you don't just create a java project then copy the HelloWorld.java we created before copy to the project.
But one of the key here is we would like to add *.so ( shared object ) in this project.
First of all we would like to double check what's the "Java Library Path". we can try the "System.out.println(System.getProperty("java.library.path"));"
if you can't find the path where you local your *.so file, you can setup as below in your java environment.
Run java project in command line
If you want to run the java in command line, you might export the java project in to a Runnable jar - using Eclipse IDE. Select the main or running class file - Launch configuration. In Library handling - select the option [ Extract required libraries in to jar file ].
Open command prompt go to the directory where runnable jar is available and run
#java -jar *.jar
BUT if you reference *.so ( C++ share objecdt ), you need to figure out the jni path as above command "System.out.println(System.getProperty("java.library.path"));", e.g.: /usr/lib. then copy your *.so to /usr/lib, otherwise your java code will be error out when you run share C++ library.
Again, this POC lab and Eclipse environment setup, I think you can complete it in 5 ~ 20 min, but be aware for the basic logic is more important to make the C++ work on Java.
Hope you enjoy it !
For the memory leak, remember garbage collection in your code.
For java heap is not big enough, you can enlarge via the link as below.
http://www.mkyong.com/eclipse/eclipse-java-lang-outofmemoryerror-java-heap-space/
PS: Run Java with Error : out of memory.
If you ran into the out of memory error, which means your code might have memory leak or the java heap memory simply is not big enough for caching the object.For the memory leak, remember garbage collection in your code.
For java heap is not big enough, you can enlarge via the link as below.
http://www.mkyong.com/eclipse/eclipse-java-lang-outofmemoryerror-java-heap-space/
Reference:
http://mrjoelkemp.com/2012/01/getting-started-with-jni-and-c-on-osx-lion/
If you gave your best, any outcome will be perfect in its own way. - (Whiplash), 2014