Author: Lao Jiu – technology big millet
Socializing: Zhihu
Public id: Lao Jiu School
Special statement: the original is not easy, without authorization shall not be reproduced or copied, if you need to reproduce can contact the author authorized
preface
The Java™ Native Interface Programmer’s Guide and Specification describes what JNI is as follows:
Planning department of my background to reference translation, please do not regard as a standard, if there is insufficient, please point out and supplement.
When the Java platform is deployed at the very top of the host environment, it enables Java applications to work with native code implemented in other programming languages, such as C/C++. Programmers are used to building applications using the traditional C/C++ language with the Java platform. Because Java applications have been co-developed with C/C++ code for many years, our Java applications can solve problems in Java applications directly using the code already inherent in C/C++. So that our Java program does not need to use Java code to rewrite again.
JNI is an advanced feature of the Java platform that we can use because it allows us to write functional code in other programming languages and be called by Java applications. As part of the Java virtual implementation, JNI provides a two-way interface that allows Java applications to call native code, as well as native code that can call Java applications. Figure 1.1 depicts JNI’s role in the virtual machine:
JNI is the interface that the Java platform is designed to give us when developing third-party applications, when we need Java applications to work with other programming languages. It is a bi-directional interface:
- Java applications call code from other programming languages through JNI (most commonly C/C++, which could theoretically be any other programming language)
- Other programming languages can also call the functionality of Java applications through JNI
Interpretation of the
Developing in pure Java is a wonderful thing for programmers. However, one drawback of Java syntax is that it is not as efficient as C/C++. Even after Java has been updated to Java 17, it is still not as fast as C/C++. The reason is simple: Java code must be executed through a Java virtual machine process! C/C++ code is code that runs directly on the native operating system!
C/C++ applications are themselves processes equivalent to Java virtual machines. In other words: Java cannot be as fast as C/C++ applications unless it has no virtual machine and is run directly by the operating system. Of course, this hypothesis is not valid.
To solve the problem of running efficiency completely, we can have Java call C functions directly to solve the problem of running efficiency. This kind of programming is called JNI programming.
While Java runs in a sandbox (virtual machine), C/C++ code runs directly in the OS, so it’s called native code.
In 1996, the first version of Java provided pure Java written string encryption library is not efficient, so only through C function encryption can solve the efficiency.
For example, in the mobile game Fengwu Three Kingdoms, which I made originally, we used hafmanga/decryption algorithm written in C, which needed to be called by JNI through Java code when installed on Android. For two reasons:
- Security – Anyone can decompile. Class bytecode files easily if we write in Java
- Efficient -Java code itself does not run as efficiently as C code
In addition, Java provides network blocking IO streams, because it is written in pure Java, so it is not up to the requirements under the environment of high efficiency. Therefore, this part of the function also needs to call local code to solve. The downside of JNI, of course, is that being developed in two languages means that native code must support different OS environments. For example, on Windows, the localcode must support the Win32 environment; Run on Linux, then localcode support Linux environment; To run on Unix, the local code must support the Unix environment.
Therefore, if we develop in win32 environment, we have to re-debug for each platform, which is very troublesome. This is why developing cross-platform (Android and iOS) mobile games using cocos2d-x’s C++ engine is the most troublesome.
Note: JNI stands for Java Native Interface.
So with the theory out of the way, let’s do some actual coding.
The first JNI application
Generally we JNI code is called C functions, of course, can also call C++ member functions. Let’s demonstrate a Java class calling a C function.
Java code
Write a HelloNative. Java source file
/** Function: write a Java class to demonstrate JNI programming - call C function
public class HelloNative{
public static native void greeting(a);
}
Copy the code
Local methods can be modified with static or not. Static is used because parameter passing is not handled.
Survival header file
We then enter the following two commands in the command
javac *.java
javah HelloNative
Copy the code
Generate a HelloNative. H header file
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class HelloNative */
#ifndef _Included_HelloNative
#define _Included_HelloNative
#ifdef __cplusplus
extern "C" {
#endif
/* * Class: HelloNative * Method: greeting * Signature: ()V */
JNIEXPORT void JNICALL Java_HelloNative_greeting
(JNIEnv *env, jclass clazz);
#ifdef __cplusplus
}
#endif
#endif
Copy the code
explain
C function header file implementation
Then we need to write a HelloNative. C source file that defines a C function for the Java virtual machine to call. Since it is written to a VIRTUAL machine, the following rules must be followed:
- Java full method names must be used, plus the package name if there is one. Such as com. Yy. HelloNative. The greeting
- Use underscores to separate each word, such as Java_HelloNative_greeting or Java_com_yy_HelloNative_greeting_
- If the class contains ASCII characters’ _ ‘, ‘$’, or digits, or Unicode encoding values greater than’ \u007F ‘, then _0XXXX hexadecimal is used
Note: If there are overloaded local methods, they must be represented by two underscores. Such as overloaded Java_HelloNative_greeting__
/** Function: Write a HelloNative. H implementation */
#include "HelloNative.h"
#include "stdio.h"
JNIEXPORT void JNICALL Java_HelloNative_greeting
(JNIEnv *env, jclass clazz){
printf("Hello Native World! \n");
}
Copy the code
C++ header file implementation
If it’s C++ then write a HelloNative. CPP file as follows
/* function: write a C++ implementation */
#include "HelloNative.h"
#include <iostream>
using namespace std;
extern "C"
JNIEXPORT void JNICALL Java_HelloNative_greeting
(JNIEnv *env, jclass clazz){
cout << "Hello Native World!" << endl;
}
Copy the code
Build dynamic link libraries
There are two ways to compile:
- Install GCC /g++ programmer (download and install at www.mingw-w64.org/doku.php)
- Install Visual Studio 2019
GCC compiler
Enter the following command in the console
gcc -Wl,--add-stdcall-alias -I"%JAVA_HOME%\include" -I"%JAVA_HOME%\include\win32" -shared -o hello.dll HelloNative.c
Copy the code
If all is well, a HelloNative. DLL file is generated
CL programming
First search to the cl.exe compiler command location and then add it to PATH.
Enter the following command in the console
cl -I "%JAVA_HOME%\include" -I "%JAVA_HOME%\include\win32" -LD HelloNative.c -FeHelloNative.dll
Copy the code
If our operating system was 64-bit, the Java call would not be successful because it is a 32-bit compiled command. The following error message is reported:
Resolve compilation process issues
There are two ways to resolve 32-bit and 64-bit incompatibilities:
- Install the 64-bit GCC compiler
- Debug the VS2019 environment to generate 64-bit DLL files
Write a hellonativetest.java source file
/** function: write a program entry to test the effect of JNI compilation
public class HelloNativeTest{
public static void main(String[] args){
HelloNative.greeting();
}
The static statement block ensures that the DLL file is loaded before the class object is created
static{
System.loadLibrary("Hello"); }}Copy the code
If all is well, we should see the following effect
Master JNI programming
Type conversion
When C and Java pass function arguments and return values back and forth, we Java actually have no primitive numeric types. Therefore, parameter escape description is carried out.
These conversions are defined in the jni.h header, which defines the constants jni _FALSE=0 and JNI_TRUE=1.
In Java, a string is a UTF-16 encoding sequence, which is equivalent to a non-null-terminated character sequence in C, so character completion is different in both languages.
JNI provides two sets of function conversion string conversion problems:
- One is a sequence of strings in UTF-8
- One is the UTF-16 value array, which is the JCHAR array
Java String corresponds to jString of JNI. For example,
If the JNI file is C++ code, then the JNIEvn class has an inline function NewStringUTF that can be called directly:
This function returns a new string jString object, which is implemented using GetStringUTFChars. We know that the Java virtual machine wants to garbage collect objects, so after our string usage is complete, we must call the function ReleaseStringUTFChars to garbage collect character objects.
Accessing object properties
Earlier we accessed the static methods of the class, along with the parameters and return values. Next we discuss accessing Java object properties.
This then needs to be implemented in CPP
Coding signature
C/C++ must have an identity code, or encoding signature, when accessing properties and methods of Java objects. For example, the “D” above means double
- If it’s an array that accesses Java, like an array of strings
- If you access a float[][] two-dimensional array
- Call a method that takes two int arguments and returns an int
- If a method is called that takes a string argument, return void
Note: Expressions end with a semicolon and there is no space between arguments.
If the construction method, such as the Employee (Java. Lang. String, Java. Util. Date)
Note: D and Ljava/util/Date; There is no gap between them. In code signature, package name. Are replaced by /.
V means the return type is void, and although Java and constructors do not return a value, the encoding signature requires V to give the virtual machine a digital signature to identify it.
Use the Javap command and participate -s to generate the encoded signature of the Java class, JAVap -S – private Employee
If all is well, the following appears
Accessing static properties
Accessing a static property is a bit like accessing a non-static property; we also use functions like GetStaticFieldID.
- Because there are no objects, we use FindClass for class references
- Use classes instead of object instances to access static properties
For example, we access the system.out static property
conclusion
JNI programming is not covered in any of the general teaching books, because it involves mixing programming with other programming languages, which is a disaster for beginner Java programmers: are our Java programs software craftsman? How can we learn C/C++ again! . Isn’t that a lie?
However, if we are going to be advanced Java programmers, we must learn to mix programming with other languages. Because no single programming language can solve every application problem, this is not a common occurrence in real development.
We hope this article will give you a quick understanding of the JNI capabilities of the Java platform and how to develop JNI in a timely and quick way to solve the problems of working with C/C++ programmers. This will not be looked down upon by C/C++ programmers, find our Java programmers should have dignity.
We can all shout out: For Dema!
The last
Remember to give dashu ❤️ attention + like + collect + comment + forward ❤️
Author: Lao Jiu School – technology big millet
Copyright belongs to the author. Commercial reprint please contact the author for authorization, non-commercial reprint please indicate the source.