The original address
Concept Introduction
Most of the computative-intensive operations will not be implemented in the Java layer, but will be processed in the Native layer. For example, some video decoding will eventually be implemented through C/C++, and then compiled into so (shared) library for Android terminal.
1.1 the JNI
JNI stands for Java native interface. It defines how Android’s bytecodes compiled from managed code (written in the Java or Kotlin programming languages) interact with native code (written in C/C++).
From the PERSPECTIVE of the JVM, there are two types of code: “Java” and “native”. Native generally refers to C/C ++. To enable Java and Native to interact, Java designed the Java Native Interface (JNI).
JNI allows Java code running inside a Java virtual machine (VM) to interoperate with applications and libraries written in other programming languages such as C++, C++, and assembly.
Although most of our software can be implemented in Java, there are some scenarios where native code is more appropriate, such as:
- Code efficiency: Higher performance with native code
- Cross-platform features: The standard Java class library does not support platform-dependent features required by an application or wants to implement a small portion of time-critical code in a lower-level language such as assembly language.
Native layer uses JNI mainly to do:
- Create, examine, and update Java objects, including arrays and strings.
- Call the Java method.
- Load the class and get the class information.
1.2 the NDK
The Native Development Suite (NDK) is a set of tools provided by Android Studio that enable you to use C and C++ code in Android applications. The default build tool for Android Studio to compile native libraries is CMake.
1.3 Cross-Compilation
Cross-compilation: The process of compiling A secondary file on platform A that platform B can execute. Such as compiling so files for Android on MacOS or Windows.
Cross-compile the Android platform libraries through the NDK provided in Android Studio.
1.4 instruction set
The CPU must comply with certain specifications when performing computing tasks. Programs must be translated into a language that the CPU can understand before being executed. This specification or language is ISA (Instruction Set Architecture).
The process by which programs are translated into the underlying code that the CPU can recognize according to the specification of some instruction set is called compile. Common instruction sets include x86, ARM, MIPS, etc., among which the instruction set can be extended. For example, the x86 instruction set can add 64-bit support, which becomes X86_64. Similarly, ARM can be extended into ARM64-V8A, and MIPS can be extended into MIPS64.
1.5 micro architecture
A microarchitecture is a concrete “implementation” of an instruction set. For example, AMD and Intel both use x86 instruction set processors, but their specific microarchitectures are different, which is a typical “implementation” problem.
The instruction set is a set of open specifications, its implementation (microarchitecture) is a very technical work, and even if you have the technology to design a microarchitecture of an instruction set, you still need to get the authorization of the instruction set, otherwise you will be sued. The design of microarchitecture directly affects the maximum frequency that the core can reach, the amount of computation that the core can perform at a certain frequency, and the energy consumption level of the core at a certain technological level, which is the quality of the microarchitecture design technology, and determines the design of the core performance.
It is important to note that just because a CPU uses the ARM instruction set does not mean that it uses the ARM microarchitecture, because Intel, Qualcomm, Apple, Nvidia and other manufacturers have developed their own microarchitecture compatible with the ARM instruction set, which is the instruction set licensing mentioned above.
1.6 ABI
The ABI is an Application Binary Interface. Different Android phones use different cpus and therefore support different instruction sets.
The main Android platforms include armeabi, ArmeabI-V7A, x86, MIPS, ARM64-V8A, MIPS64, x86_64:
x86/x86_64
CPU architecture used by 32-bit / 64-bit computers. X86_64 is compatible with x86_64 and x86
armeabi
Incompatible with ARMv5 and V6 devices. Compatible with ArmeABI-V7A and ArmeABI
armeabi-v7
A 7th generation or higher ARM processor that is used by most Android devices manufactured after 15, 2011
arm64-v8a
Compatible with ARM64-V8A, ArmeabI-V7A, Armeabi, the main difference is 64-bit support (Google Store mandatory arm64-V8A from August 2019)
Android Studio builds linked libraries for all platforms by default. If you don’t need to be compatible with certain architectures, you can specify the architectures to be compiled in the Gradle script of your app, so that when AS is compiled, platforms that are not declared will not be compiled.
android { ... defaultConfig { ... NDK {// Set the supported SO library architecture (developers can choose one or more platform SO as required) abiFilters "armeabi-v7A "," arm64-v8A "}}}Copy the code
Ii. Engineering Practice
2.1 New Construction project
Make sure that you are running Android Studio version 4.0 or higher. If you are running Android Studio version 4.0 or higher, you will need to configure the Makefile by hand with the Android.mk file and manually compile the SO library.
In the new version of Android Studio, when creating the project, select the final Native C++, then keep the C++ Standard default, Android Studio will automatically create the JNI environment.
2.2 Existing Projects
Note: The NDK has been configured
Create a CPP directory under app/ SRC /main and create a cmakelists. TXT file and a native-lib. CPP file under this directory as shown in the following image (don’t worry about the lame folder, it is needed for my own project) :
# For more information about using CMake with Android Studio, read the # documentation: https://d.android.com/studio/projects/add-native-code.html # Sets the minimum version of CMake required to build the Native Library.cmake_minimum_required (VERSION 3.4.1) # Creates and Names a library sets it as either STATIC # or SHARED, and provides the relative paths to its source code. # You can define multiple libraries, and CMake builds them for you. # Gradle automatically packages shared libraries with your APK. add_library( # Sets the name of the library. native-lib # Sets the library as a shared library. SHARED # Provides a relative path to your source file(s). native-lib.cpp ) # Searches for a specified prebuilt library and stores the path as a # variable. Because CMake includes system libraries in the search path by # default, you only need to specify the name of the public NDK library # you want to add. CMake verifies that the library exists before # completing its build. find_library( # Sets the name of the path variable. log-lib # Specifies the name of the NDK library that # you want CMake to locate. log) # Specifies libraries CMake should link to your target library. You # can link multiple libraries, such as libraries you define in this # build script, prebuilt third-party libraries, or system libraries. target_link_libraries( # Specifies the target library. native-lib # Links the target library to the log library # included in the NDK. ${log-lib})Copy the code
Specify the cmake build file path in app/build.gradle
android { ....... ExternalNativeBuild {cmake {path "/ SRC/main/CPP CMakeLists. TXT" version "3.10.2"}}}Copy the code
Native-lib. CPP modifies the contents of the file to implement a simple C++ method that returns a string Hello from C++ :
#include <jni.h>
#include <string>
static lame_global_flags *glf = NULL;
extern "C"
JNIEXPORT jstring JNICALL
Java_com_lucas_audioSample_view_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) {
// TODO: implement stringFromJNI()
std::string hello = "Hello from C++";
// return env->NewStringUTF(hello.c_str());
return env->NewStringUTF(get_lame_version());
}
Copy the code
You can now call native methods in MainActivity:
static {
System.loadLibrary("native-lib");
}
public native String stringFromJNI();
Copy the code
Click Build and run and Gradle adds a CMake or NdK-build process as a dependency that compiles and builds native libraries and packages them with APK.
Generate So library
At the end of the project, you can save the methods in C\C++ to make an SDK. At this point, you need to generate the So library.
Generating the So library sounds daunting, but after the demo in Chapter 2 runs, the So library is automatically generated. Open the project intermediates->cmake and you can see the generated SO library file.
Create a SRC sibling directory libs in your project and copy the so library file into it.
Under build.gradle add:
android {
.......
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
}
Copy the code
If you don’t want your CPP code to be seen by others, you can directly delete the CPP directory under MAN and code directly through the compiled so library.
Dynamic loading of SO library
Loading the SO library statically has some limitations:
- Increase apK volume size
- Multiple SDK so files loading at the same time may cause conflicts
- Individual SO compatibility is not very good for different SDK versions
- The SO library cannot be updated dynamically unless a new version is released
In view of the above problems, we find that static loading SO is not flexible enough. All SO files are welded to APK package when installing software, which is not easy to expand. Is there any way to solve or optimize these problems? Yes, that is dynamic load shared link library.
4.1 What is Dynamic loading
The required SO library is not directly copied into the APK package, but placed in the cloud. After the user installs APK and uses it, the user’s CPU architecture is judged first, and then the so library file of the user’s phone with the optimal ABI architecture is downloaded to the cloud at the appropriate time through some strategy. Then, when users actually need to use native functions, manual system.load (lib), and finally use native methods or data structures, so that dynamic loading is completed.
4.2 Advantages of Dynamic loading
- The APK volume is greatly reduced and only the SO library of the current ABI is required
- Update the SO library dynamically
4.3 Implementation Procedure
- Generate the required SO library files for various ABI versions and save them in the cloud
- Design some policy that triggers the initialization task at a specific time
- The initialization task is passed first
Build.CPU_ABI
Gets the optimal ABI schema for the current device, exceptBuild.CPU_ABI
And the second ABI, passBuild.CPU_ABI2
SUPPORTED_ABIS is recommended after SDK21, which is an array. The more advanced the array elements are, the better ABI of the current device is - Go to the cloud to download the SO file of the corresponding ABI type to the user space of the local APP, for example:
getDir("libs", Context.MODE_PRIVATE).getAbsolutePath()
, the corresponding path isdata\data\package\app_libs\
- Call when using native functionality
System.load(lib)
Method to load the so library file you just downloaded,lib
Pair path for the library file you just downloaded - Call native method validation
4.4 summarize
Although dynamic loading can make loading so library more flexible and more customized, it also has disadvantages:
- When to download the so file? Strategies that need to be developed
- Download time, need to consider network exceptions and other problems,
- The scenarios considered by the policy must be complete: in addition to network factors, all scenarios need to be considered
- Reduced user experience: Users wait time to download the SO library when they first use the function
Therefore, in practical development, it is best to use static loading and dynamic loading together. Dynamic loading is only used for scenarios with deep business hierarchies and low frequency of use.
Reference article:
developer.android.com/ndk/guides
Developer. The android. Google. Cn/the NDK/guides /…
The basics of Android – JNI development you need to know
Hymane.itscoder.com/dynamic-loa…