Introduction to the

It is believed that every programmer has a dream to become a C++ master. After all, C++ programmers are at the top of the programmer contempt chain, and they can look down on programmers of any other language.

But the truth is that countless programmers have gone from being a beginner to quitting, given the difficulty of C++, to switching to Java. JAVA, with its big heart, has embraced programmers who can’t make it to the top of C++.

Joking, the advantage of C and C++ lies in the interaction with the bottom of the system and the speed and efficiency of its operation, while the advantage of JAVA lies in the extensive application framework, which can quickly build the required application program. Each has its strengths.

The advantage of the framework is that it makes application development easier and allows applications to be copied in batches quickly.

As you know, the UNDERLYING JVM is written in C and C++, and JAVA bytecode is suitable for JVM interaction, so it seems intuitive that JAVA can interact with the underlying C++ code. So how do you interact? Is it going to be complicated?

Today, this article takes you to reveal one by one.

JDK native methods

Local methods are methods that call the operating system or other underlying libraries. These methods are external interfaces to the system and are used for interaction between programs and the operating system. Think about it, what native methods are there in the JDK?

The first thing that comes to mind is file operations, because file operations must rely on the IO interface provided at the bottom of the system. Let’s take a look at the implementation of File delete method:

public boolean delete() { @SuppressWarnings("removal") SecurityManager security = System.getSecurityManager(); if (security ! = null) { security.checkDelete(path); } if (isInvalid()) { return false; } return fs.delete(this); }Copy the code

The delete method on File first calls the SecurityManager for permission checks to see if it can be deleted. If it can be deleted, FileSystem’s delete method is continued.

Moving on to FileSystem’s delete method:

public abstract boolean delete(File f);
Copy the code

You can see that the DELETE method in FileSystem is an abstract method that requires a concrete implementation.

This implementation is platform-dependent. If you are a Linux or MAC operating system, the implementation class is UnixFileSystem and its delete method is as follows:

    public boolean delete(File f) {
        if (useCanonCaches) {
            cache.clear();
        }
        if (useCanonPrefixCache) {
            javaHomePrefixCache.clear();
        }
        return delete0(f);
    }
    private native boolean delete0(File f);
Copy the code

As you can see, the delete method ends up calling the delete0 method, which is a native method, meaning that it needs to call a system-native method.

The JDK provides an implementation of JAVA calling Native system methods called JNI, which stands for JAVA Native Interface and was introduced in JAVA1.1. It allows Java code to interact with code written in other languages.

To verify the feasibility of JNI, let’s implement a native method ourselves and call it in Java to see if it works.

Custom native methods

It is easy to define native methods in JAVA, just add the native keyword in front of the method description, this method does not require any implementation. Here’s a concrete example:

public class JNIUsage { public native void printMsg(); Public static void main(String[] args) {// Load C file system.loadLibrary ("JNIUsage"); JNIUsage jniUsage = new JNIUsage(); jniUsage.printMsg(); }}Copy the code

In the above example, we define a native printMsg, and then load the Library file containing the implementation in main, which can then be called just like a normal JAVA method.

So how to implement this native method?

Those familiar or unfamiliar with C++ should have heard of the concept of an overfile. Generally, we define the methods we want to implement in a header file, and then implement the methods defined in the header file in a concrete content file.

So you need to include the printMsg method in the header file, and you can generate the header file using the javah command.

First go to the root directory of the JNIUsage source file and run the following command:

javah -classpath . -jni com.flydean.JNIUsage
Copy the code

This command generates a com_flydean_jniusage.h file in the root directory of the project source code. Open it and see the details as follows:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_flydean_JNIUsage */

#ifndef _Included_com_flydean_JNIUsage
#define _Included_com_flydean_JNIUsage
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_flydean_JNIUsage
 * Method:    printMsg
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_flydean_JNIUsage_printMsg
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif
Copy the code

In a nutshell, the head file defines a Java_com_flydean_JNIUsage_printMsg method that needs to be implemented.

Next, we need to implement this header file.

Here we use JetBrain’s Clion development tools to create a c++ project:

Note that the type of this project needs to be shared.

Then copy the com_flydean_jniUsage. h file to the root directory of the project.

Jni. h and jni_md.h files in the JDK home directory include (win32 for Windows, Darwin for MAC). Copy to the root directory of the project.

So the compilation errors are gone.

Finally, we modify the default library.cpp file to introduce com_flydean_jniusage.h and implement it as follows:

#include "com_flydean_JNIUsage.h" #include <iostream> JNIEXPORT void JNICALL Java_com_flydean_JNIUsage_printMsg (JNIEnv *, jobject){ printf("this is www.flydean.com!" ); }Copy the code

So far, the project’s code structure should look like this:

Build –> build ‘JNIUsage’ to generate libjniUsage.dylib:

====================[ Build | JNIUsage | Debug ]================================
/Applications/CLion.app/Contents/bin/cmake/mac/bin/cmake --build /Users/flydean/data/git/cplus/JNIUsage/cmake-build-debug --target JNIUsage
[2/2] Linking CXX shared library libJNIUsage.dylib

Build finished
Copy the code

With libjniusage.dylib, we also need to add it to the path in our JAVA project:

Select the Java-Jni module, select JARs or Directories in the dependency, and select the libjniUsage.dylib directory you just found.

Once saved, you can run the JAVA code with the following result:

/ Library/Java/JavaVirtualMachines/JDK - 17.0.1. JDK/Contents/Home/bin/Java -Djava.library.path=/Users/flydean/data/git/cplus/JNIUsage/cmake-build-debug -Dfile.encoding=UTF-8 -classpath /Users/flydean/data/git/learn-java-base-9-to-20/java-jni/target/classes: com.flydean.JNIUsage this is www.flydean.com!Copy the code

Or you can add libjniUsage. dylib to the Classpath where Java is running from the command line.

conclusion

This is a simple example of using JAVA to call native methods. As you can see, the steps are quite complicated, so is there an easier way for JAVA to call native methods? Yes, this is JNA, which we’ll cover in more detail in a future article.

The code for this article can be found at github.com/ddean2009/l…

This article is available at www.flydean.com/01-jni-over…

The most popular interpretation, the most profound dry goods, the most concise tutorial, many tips you didn’t know waiting for you to discover!

Welcome to pay attention to my public number: “procedures those things”, understand technology, more understand you!