You are on page 1of 41

Java Native Interface

Lecture 7

JNI intro: https://www.youtube.com/watch?v=0FI7AitQJQ4


Introduction
 Java Native Interface (JNI) is a programming framework
that is part of the Java Developer Kit (JDK).
 JNI interoperate with:
 Native applications (program specific to a hardware and
operating system platform)
 Libraries written in other programming languages (such as C,
C++)
General Workflow of JNI

Source: http://electrofriends.com/articles/jni/jni-part1-java-native-interface/
Java and JNI
 Java applications do not run directly on the hardware, but
actually run in a virtual machine.
 Java source code is compiled to a bytecode file and it is
executed by the virtual machine. This makes Java cross-
platform capability.
 However, the cross-platform capability limits its
interaction with the local machine's internal components,
making it difficult to use the local machine instructions to
run an existing software library.
 JNI make Java code and native code (C/C++) collaborate
and share resources.
Java Compilation
Step 1: Write Java source code Editor

Source code (xxx.java)

Step 2: Compile java source


code into bytecode file Java Compiler

Bytecode file (xxx.class)

Step 3: Execute bytecode file Java Virtual


Machine (JVM)

Output
JNI Compilation
Step 1: Write Java source code Editor
Source code with native declaration (xxx.java)
Step 2: Compile Java source code
to generate the bytecode file and
Java Compiler
C++ header file • Bytecode file (xxx.class)
• C++ header file (xxx.h)
Step 3: Write C++ native code
C++ native code (cxx.cpp)
Step 4: Build shared library file
Shared library file (cxx.dll)

Step 5: Execute bytecode file Java Virtual Output


(need shared library file) Machine (JVM)
Usage of JNI
 To leverage platform specific features outside a JVM.
 To communicate with device driver written in C/C++ or
assembler that used to access the hardware
 To utilize existing code libraries in C/C++ programs.
 For example, there might be a really good file compression
library written in C/C++. Why try to rewrite it in Java when it
can be accessed by JNI?
 JNI defines a way for the bytecode that Android OS
compiles from managed code (written in the Java or
Kotlin programming languages) to interact with native
code (written in C/C++).
Drawback of JNI
 The program is no longer platform independent
 The program is not robust
 If there is a null pointer exception in the native code, the
JVM can not display a helpful message. It might even lock
up.
 Difficult to debug runtime error in native code
JNI Type
 JNI is a two-way interface that allows Java
applications to invoke native code (C/C++) and vice
versa.

 There are two types of JNI: focus point


 calling C/C++ code from Java main program
 calling Java code from C/C++ main program
Tools and Components

Java Compiler (Java SE Development Kit –


version 8 and above – 64 bit)
Two main compilers for JNI

C/C++ Compiler (MinGW-w64)

Text Editor (Notepad/Notepad++) The place to write the


source code

Command Prompt (Windows) / Terminal The place to perform the


(MacOs, Ubuntu) program compilation and
execution
Calling C++ from Java (using JNI)
1. Write the Java source code (.java file)
 The Java source code consist of native method declaration.
2. Compile the Java source code
 Generate the Java bytecode file (.class file)
 Generate the C++ header file (.h file)
 The C++ header file defines native method signature.
3. Write the C++ native code (.cpp file)
 To write the code for body of the native method.
4. Build the shared library file (.dll file)
 It create shared library file from C++ native code in step 3
5. Execute the Java program
All the above files must be located in the same directory
Native Method/Function
 A method that is native is implemented in platform-
dependent code.
 The code is written in another programming language
such as C, C++, or others.
 To use native methods in Java code:
 To write a native method declaration for each native method
in Java source code.
 To load the native code library explicitly in Java code.
 To provide the implementation of native method in C++
code.
JNI Example
// Happy.java

class Happy {
public native void printText (); 1 [native method declaration]

static {
[load 2 System.loadLibrary ("happy");
the sam
} en
native am
code e
public static void main (String[] args) {
library] Happy p1 = new Happy ();
p1.printText ();
}
} // happy.cpp 3 [implementation of native method in C++ code]

#include <iostream>
#include "Happy.h"

JNIEXPORT void JNICALL Java_Happy_printText(JNIEnv *env, jobject


obj) {
std::cout << "Happy New Year !\n";
return;
Native Method/Function cont..
 In the JNI framework, the native method is implemented
in separate C++ file (with .cpp extension).
 The native method is called in the Java file (with .java
extension).
 When the JVM invokes the native method, it passes
a JNIEnv pointer, a jobject pointer, and any Java
arguments declared by the Java method.
JNIEXPORT void JNICALL Java_ClassName_MethodName
(JNIEnv *env, jobject obj) {
/*Implement Native Method Here*/
}

Implementation of Native Method in the C++ File


JNI Example: Hello World!

Write Java
Hello.java
source code
JNI Example: Hello World!

Write Java
Hello.java javac
source code

Hello.h Hello.class
JNI Example: Hello World!

Write Java
Hello.java javac
source code

iostream.h Hello.h Hello.class

Write C++
native code

CHello.cpp
JNI Example: Hello World!

Write Java
Hello.java javac
source code

iostream.h Hello.h Hello.class

Write C++
native code

CHello.cpp g++ CHello.dll


JNI Example: Hello World!

Write Java
Hello.java javac
source code

iostream.h Hello.h Hello.class

Write C++ java “Hello, World!”


native code

CHello.cpp g++ CHello.dll


JNI “Hello World!” Program
1. Write a Java source file (Hello.java) that declares the
native method.
2. Use javac to compile Hello.java, and it generates class file
(Hello.class) and C++ header file (Hello.h) containing the
native function signature.
3. Write the C++ native code (CHello.cpp) to implement the
native method.
4. Build the shared library file (CHello.dll) from C++ native
code in step 3.
5. Execute the Hello program using the java runtime
interpreter.
 Java class file (Hello.class) and native shared library (CHello.dll) are
loaded at the runtime.
Step 1: Create Java source code (Hello.java)
 Java file defines a class named Hello that contains a call to
the native method named as print().
class Hello {
// declare a native method print( ) that receives nothing and returns void
// without any implementation
public native void print( );

static // static initializer code


{
System.loadLibrary("CHello"); // load shared library at runtime
} // CHello.dll (Windows)

public static void main(String[] args) {


Hello hw = new Hello( ); // create Java object
hw.print( ); // use Java object to invoke the native method
}
}
Step 1: Create Java source code (Hello.java)
 Static initializer invokes System.loadLibrary( ) to load the
native shared library "CHello" (which contains the native
method print( )) during the class loading.
 It will be mapped to "CHello.dll" in Windows platform.
 The method print( ) is a native method, via keyword
native, which denotes that this method is implemented in
another language.
 a native method cannot have a body.
 The main( ) method create an instance/object of Hello and
invoke the native method print( ).
Step 2: Compile Java source code
 Place the Java source code in the Desktop.
 In the command prompt, change the directory into Desktop,
use command “cd”
 To compile the Java source file, use command “javac”:
javac -h . Hello.java
 No error message means the compilation is success.
 The Hello.class is produced and placed in the Desktop (same
place with the Java source code)
Step 2: Compile Java source code cont..
 The content of the C++ header file

The header declares a C/C++ function


Java_Hello_print as follows:

JNIEXPORT void JNICALL


Java_Hello_print(JNIEnv *, jobject);

native method name The naming convention for C/C++ function


is Java_{classname}_{method_name} (JNI
arguments).
Java class name
Step 2: Compile Java source code cont..
 The arguments:
 JNIEnv*: reference to JNI environment, which lets the C/C++
file
access all the JNI functions.
 jobject: reference to "this" Java object, the object created in
Java file
 The macros of JNIEXPORT and JNICALL:
 Both enable the related functions to be exported in the
shared library with the appropriate linkage.
Step 3: Write C++ native code (CHello.cpp)
 Write C++ native code using Notepad
 Copy the native method signature from the C++ header file.
JNIEXPORT void JNICALL Java_Hello_print (JNIEnv *, jobject);
 Write the content of the native method.
#include "Hello.h" // link to the C++ header file
#include <iostream> // implements the std::cout function

// To implement the native method of Hello class


JNIEXPORT void JNICALL Java_Hello_print(JNIEnv *env, jobject obj) {
std::cout << "Hello World!\n";
return;
}

 The std::cout function is used to display the string “Hello


World!”.
 Save the C++ native code as "CHello.cpp".
Step 4: Generate shared library file
(CHello.dll)
 Build the shared library file from C++ implementation code.
 The shared library file has a DLL extension if the C++ native code
run under Windows platform.
 In command prompt, type the command as below:

The compiler options used are:


- I: to specify the header files directories.
- shared: to generate share library.
- o: for setting the output filename "CHello.dll".

Assume Java compiler is located at C:\Program Files\Java\jdk-17.0.3.1


Step 5: Execute Hello Program
 Place the Hello.class and CHello.dll in the same folder.
 Execute the Java class (main method) as shown below:
java HelloWorld
 You should see “Hello World!” appear on the screen!

 If the error message “java.lang.UnsatisfiedLinkError”


appears, this means Java could not find the shared library
(.dll file) OR mismatch in native functions.
Error Message of JNI
What is the main cause to have such error messages below ?
Error message 1:

Error message 2:
Primitive Types and Native Equivalents
 Each element in Java language must have a corresponding
native counterpart.
Object Types and Native Equivalents
Object Mappings Example
In the Java source code:
class Book {
private native String read(String prompt);
... da t
at
yp
} eo
data type of return value f in
pu
tp
a ra
me
te r
In the C implementation code:
JNIEXPORT jstring JNICALL Java_Book_read(JNIEnv *, jobject, jstring);

“Java_” prefix + class name + “_” + method name


Accessing Java Strings
class Book {
private native String read (String prompt);
...
}
// To display the input argument of read function in the C++ implementation code
JNIEXPORT jstring JNICALL Java_Book_read(JNIEnv *env, jobject obj, jstring
text)
{
std::cout << text;
...
}
 It is wrong to use the jstring as C-string directly in native code.
 The jstring type in JNI is not equivalent to the string type in C/C++.
 jstring must be converted to C-string using appropriate JNI function.
Accessing Java Strings
 The jstring type is strings type used in Java virtual
machine, and is different from the regular C-string type.
 jstring cannot be used as a normal C-string.
 The jstring need to be converted to C-string through JNI
functions.
 Two type of character encoding format:
 Unicode : 16-bit characters
 UTF-8 : encoding scheme that is compatible with 7-bit ASCII
strings
Accessing Java Strings cont..
/* Right way */
JNIEXPORT jstring JNICALL Java_Book_read(JNIEnv *env, jobject obj, jstring text) {
// to convert the jstring format to a C-style string format and
// the value of text (jstring) is stored to the str (C-style string)
const char* str = env->GetStringUTFChars(text, NULL);
std::cout << "The book title is " << str << "\n";

/* release the memory allocated for the string operation */


env->ReleaseStringUTFChars(text, str);

string title;
cout << "\nEnter the new book title: ";
getline(cin, title);
jstring result = env->NewStringUTF(title.c_str()); // to create a new jstring instance and
// store it into variable result
return result;
}
JNI String Functions
 The JNI provides functions to obtain the Java string
format (Unicode and UTF-8) as shown below.
JNI Function Description
GetStringChars Obtains or releases a pointer to the contents of a string in
ReleaseStringChars Unicode format. May return a copy of the string.
GetStringUTFChars Obtains or releases a pointer to the contents of a string in
ReleaseStringUTFChars UTF-8 format.
NewString Creates a java.lang.String instance that contains the same
sequence of characters as the given Unicode C string.
NewStringUTF Creates a java.lang.String instance that contains the same
sequence of characters as the given UTF-8 encoded C string.
GetStringLength Returns the number of Unicode characters in the string.
GetStringUTFLength Returns the number of bytes needed to represent a string in
the UTF-8 format.
Extra Exercise (Q1)
// Q1.java
import java.util.Scanner;

class Q1 {
public native void printTest(int num);

static {
System.loadLibrary("CQ1");
}

public static void main(String[] args) {


Scanner ab = new Scanner(System.in);
System.out.printf("Please enter your magic number: ");
int num = ab.nextInt();

Q1 ww = new Q1();
ww.printTest(num);
}
}
Extra Exercise (Q1)
// CQ1.cpp
#include "Q1.h"
#include <iostream>

JNIEXPORT void JNICALL Java_Q1_printTest(JNIEnv *env, jobject obj, jint


nn) {
std::cout << "The magic number is " << nn << "\n";
return;
}
Extra Exercise (Q2)
// Q2.java
import java.util.Scanner;

class Q2 {
public native void printPyramid(int num);

static {
System.loadLibrary("CQ2");
}

public static void main(String[] args) {


Scanner ab = new Scanner(System.in);
System.out.printf("Please enter number of rows: ");
int num = ab.nextInt();

Q2 ww = new Q2();
ww.printPyramid(num);
}
}
Extra Exercise (Q2)
#include "Q2.h"
#include <iostream>

JNIEXPORT void JNICALL Java_Q2_printPyramid(JNIEnv *env, jobject obj, jint nn)


{

for (int i = 1; i <= nn; ++i) {


for (int j = 1; j <= i; ++j) {
std::cout << "* ";
}
std::cout << "\n";
}
return;
}
Extra Exercise (Q2)

You might also like