Android interview in the old is to ask JNI, but I move bricks in a small factory for many years, can not how used ah cry ~~~~ used to understand it.

Edited by guuguo proofread by GuuguoCopy the code

Noun explanation

  • C + + header fileThe: header file is used to place the correspondingc++Method declaration, in fact, its content is the same as the content of the.cpp file, are C++ source code. But the header file is not compiled. Header files can pass#includeIs included in the.cpp file. Include simply copies the header definition code into a. CPP file. So header files are used to place declarations, not definitions. Definition conflicts occur when multiple source files contain definitions directly, while declarations do not. Header files can also contain definitions, but try not to, if necessary, pass#ifndef... #endifLet the compiler determine if a name is defined and then decide whether to continue compiling.
  • JNI (Java Native Interface)A programming framework that allows Java programs in a Java virtual machine to call local applications/libraries and be called by other programs.
  • CMakeIs a cross-platform build tool, support C/C++/Java language engineering build. Used in this article to compile c++ code.

What is the passage about?

A large number of implementations in Android system are native implementations, and Java layer calls are made through JNI. Learning to use JNI will not only help us develop and interview, but also add two more bricks to our understanding of the android source code foundation. State the content and purpose of the passage:

  1. Understand the basic use of JNI in development
  2. Java code and c++ native method link principle
  3. What is the JNI framework? What is it
  4. What is the Ndk?

Understanding these four little points gives you an initial understanding of JNI that you can use when developing with it.

Small chestnut used by JNI (static registration)

Jni registration can be divided into static registration and dynamic registration.

  • Static registration: Find the corresponding JNI function based on the function name, style isJava_ package name _ Class name _ method name
  • Dynamic registration: when we useSystem#loadLibararyMethod loads the so library, and the Java virtual machine finds itJNI_OnLoadFunction and actively call. So we can do it inJNI_OnLoadcalljniRegisterNativeMethodsPerform dynamic registration of methods. (If you don’t learn this method, please Google)

Let’s talk about static registration first:

  1. Create the Demo JNI SDK module

Let’s create asdkModule, carrying native and JNI codes, directory structure is as follows:

The main directories shown in the figure are as follows:

  • src/main/javaJava source code
  • src/main/jniNative source
  • src/main/jni/CMakeLists.txtCmake configuration file

Configure the jNI source path in build.gradle:

sourceSets {
    main {
        jni.srcDirs = ['src/main/jni']}}Copy the code
  1. Define native Java methods

In Kotlin, the keyword external is used to identify the method as a JNI method. When the method is called, the Java_ package name _ class name _ method name c++ function. Let’s first create the JNI entry Java class jni. Java, define the Java native method. The method is as follows:

package top.guuguo.myapplication
class JNI {
    /** Returns the signed string */
    external fun signString(str: String): String
    companion object {
        /// The instance must be created after the native code is loaded, as in this example
        ///System.loadLibrary("jni-test")
        val instance by lazy { JNI() }
    }
}
Copy the code

We define a simple native method signString that mimics the method of signing a string.

  1. Generate the corresponding header file for the pair

Javah tools are provided in Java. It can automatically generate native methods corresponding to c++ header files. Take a look at the instructions for the tool using Javah-h:

Javah [options] <classes> where [options] includes: -o <file> Output file (either -d or -o can only be used) -d <dir> Output directory -v -verbose Enable detailed output -h --help -? -version Output version information -jni Generates a JNI-style header file (default) -force always writes to the output file -classpath <path> the path from which the class is loaded -cp <path> the path from which the class is loaded -bootclasspath <path> The path from which to load the bootclass <classes> is specified using its fully qualified name (for example, java.lang.object).Copy the code

It can be used as follows: -cp is equivalent to -classpath, which specifies the class file path to generate the header file

javah -d app/src/main/cpp/header -cp "./app/build/tmp/kotlin-classes/debug/"  top.guuguo.myapplication.JNI
Copy the code

You can see that the.h file was successfully generated after the command was executedThere are the.hAfter the JNI declaration file, we completed the implementation of the corresponding method in jni. CPP, the code is as follows:

#include <stdio.h>
#include <stdlib.h>
#include <string>
#include "header/top_guuguo_myapplication_JNI.h"

JNIEXPORT jstring JNICALL Java_top_guuguo_myapplication_JNI_signString(JNIEnv *env, jobject obj, jstring jStr) {
    const char *cstr = env->GetStringUTFChars(jStr, NULL);
    std::string str = std::string(cstr);
    env->ReleaseStringUTFChars(jStr, cstr);
    std::string cres = "signed:" + str;
    jstring jres = env->NewStringUTF(cres.c_str());
    return jres;
}
Copy the code

The definition implementation of the method is simple, just concatenating the signed: string in front of the string passed in.

  1. Perfect cmakelist. TXT and build.gradle compilation. So product

There are currently two options for compiling native source code: cmake and NDK-build. CMake is a little more popular, so let’s introduce CMake. CMake is a cross-platform build tool that supports engineering builds in C/C++/Java languages. By configuring the CMake build script cmakelists. TXT, we can use the CMake command to do the custom compilation. This is the main instruction used by cmake

  • set(all_src "./src"): The directive can be namedall_srcThe value of the variable
  • add_libraryThe main effect of this directive is to generate a link file from the specified source file and then add it to the project

CMakeLists.txt

Let’s edit the configuration file to use the following

# Copyright (c) 2019 - 2020 The Alibaba DingTalk Authors. All rights reserved.PROJECT (jni - test) cmake_minimum_required (VERSION 3.4.1 track)
#Assign to some c++ compile-time identifiers
#set(CMAKE_CXX_COMPILER      "clang++" )         # display the C++ compiler specified
#set(CMAKE_CXX_FLAGS   "-std=c++11 -O2")             # c++11
#set(CMAKE_CXX_FLAGS   "-g")                     # Debug message
#set(CMAKE_CXX_FLAGS   "-Wall")                  # Enable all alerts
#set(CMAKE_CXX_FLAGS_DEBUG   "-O0" )             Debugging packages are not optimized
#set(CMAKE_CXX_FLAGS_RELEASE "-O2 -DNDEBUG " )   # Release package optimization
set(CMAKE_CXX_FLAGS_RELEASE "-std=c++11 -O2 ")
set(CMAKE_CXX_FLAGS_DEBUG "-std=c++11 -O2 ")

#Assign to the variable SRC_ROOT
set(SRC_ROOT "./")

#Traverses all. CPP files directly under the directory and saves them in variables
file(GLOB all_src
        "${SRC_ROOT}/*.hpp"
        "${SRC_ROOT}/*.cpp"
        "${SRC_ROOT}/src/*.h"
        "${SRC_ROOT}/src/*.hpp"
        "${SRC_ROOT}/header/*.h"
        "${SRC_ROOT}/header/*.hpp"
        )
#Add the source file to the build dynamic library
add_library(jni-test SHARED ${all_src})
Copy the code

Build. Gradle adds native configuration:

defaultConfig {
    / * *... * /
    externalNativeBuild {
        cmake {
            /// Compile target name
            targets 'jni-test'
            // Precompiled behavior configuration :-fexceptions enables exception handling
            cppFlags "-std=c++11 -fexceptions -frtti"
            arguments "-DANDROID_STL=c++_shared"
        }
    }
}
externalNativeBuild {
    cmake {
        version '3.6.0'
        path 'src/main/jni/CMakeLists.txt'}}Copy the code

Specify the necessary parameters in the code above, along with the cmake version and configuration file path

Compile:

The following compilation will automatically compile the relevant libraries, or you can directly package the corresponding so libraries and AAR packages with the gradle command

./gradlew :sdk:aR
Copy the code

Also is to use aR (assembleRelease) commands to compile the release package, in the build/intermediates/cmake/release can find corresponding product.

  1. Simple c++ method calls

With the definition completed, we simply implement the call:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        System.loadLibrary("jni-test")
        findViewById<Button>(R.id.button).setOnClickListener {
           Toast.makeText(this,JNI.instance.signString("hello world"),Toast.LENGTH_LONG).show()
        }
    }
}
Copy the code

After we click the button, directly pop up the string after the toast display signature.

This one needs to be noticed!!

The steps to get the JNI instance need to come after System.loadLibrary. In this way, the corresponding native method can be called correctly.

Summary:

At this point, a JNI sample of the minimization implementation is complete, implementing the native method definition and Java calls to it. And with that, we can go a lot further in the future

  • We can learn more about cross-platformnativeHow the SDK works in Android.
  • Ability to read aOSP source code to increase their own basic skills

How do Java code and c++ native methods connect

When Java invokes native methods, the ART VM performs special processing accordingly. Refer to the implementation process of the Android ART class method. The VIRTUAL machine determines whether the method is native and executes the method. The implementation of the client side is very simple, is the above mentioned static registration and dynamic registration.

What is the JNI framework and what does it include?

JNIEnv represents the context in which Java calls native languages and is a pointer that encapsulates almost all JNI methods. We see the jni. H source (aosp source path source/libnativehelper/include_jni/jni. H). Find the definition of JNIEnv: typedef _JNIEnv JNIEnv; You can see that this is an alias of type _JNIEnv. _JNIEnv = _JNIEnv

truct _JNIEnv {
    /* do not rename this; it does not seem to be entirely opaque */
    const struct JNINativeInterface* functions;
    #if defined(__cplusplus)
    jint GetVersion(a)
    { return functions->GetVersion(this); }
    jclass DefineClass(const char *name, jobject loader, const jbyte* buf,
        jsize bufLen)
    { return functions->DefineClass(this, name, loader, buf, bufLen); }
   // ...
    }
Copy the code

It can be seen that all JNIEnv methods are indirectly called methods of JNINativeInterface, just a layer of encapsulation of JNINativeInterface structure. Most of our JNI operations go through it.

What is NDK and how does it relate to JNI?

ndk:Native Development Kit
Copy the code

The Android NDK supports compiling application C and C++ code using CMake. The NDK is a collection of tools.

  • The NDK provides a set of tools to help developers quickly develop dynamic libraries for C (or C++) and automatically package so and Java applications together as APK. These tools can be tremendously helpful to developers.
  • The NDK integrates a cross-compiler and provides mk files to isolate differences in CPU, platform, ABI, etc. Developers can create SO by simply modifying mk files (indicating “which files need to be compiled”, “compile feature requirements”, etc.).
  • The NDK can automatically package so and Java applications together, greatly reducing the developer’s packaging effort.

The NDK provides a stable, limited API header declaration. Including: C11 standard library (libc), standard mathematics library (libm), c++17 library, Log library (liblog), compression library (libz), Vulkan rendering library (libvulkan), openGl library (libGLESv3) and so on. NDK can generate C/C++ dynamically linked libraries for us. Our development of Native is NDK based development.

NDK has nothing to do with JNI, but is a dynamic library based on NDK that needs to be communicated through JNI and Java.

The last

Jni: What do you mean by “123”?

  1. How to associate jNI native code? Through static registration and dynamic registration.
  2. What should I pay attention to when loading the SO library? The corresponding implementation can be called only after obtaining the instance of System. LoadLibrary and calling the native method.
  3. How to build so library? The NDK supports code compilation and build through cmake.
  4. What is the difference between NDK and JDK?

Only learning can be my growth, only learning can be my progress, I want to study hard, to contribute to the construction of the motherland ~~~

Reference article:

  • Android JNI Introduction (eight) – The use of CMakeLists
  • JNI method registration and loading principle analysis
  • JNI implementation source code analysis
  • The process by which Android ART executes class methods