In the first article of 2018, I mainly explained some concepts of JNI and NDK and how to use CMake build tools to develop NDK in AS 3.0 environment.
1. Understand some concepts
-
JNI(Java Native Interface):
Java native interface is a bridge between Java and other native code languages (such as C and C++).
-
NDK(Native Development Kit):
Native Development Toolset is a set of tools that allow you to implement program functions using native code languages such as C and C++.
-
ABI(Application Binary Interface):
Application binary interface, different CPUs support different instruction sets, each combination of CPU and instruction set has its own application binary interface (or ABI), ABI can very accurately define how the machine code of application interacts with the system at run time.
Supported ABI: armeabi, armeabi-v7a, arm64-v8a, x86, x86_64, mips, mips64
-
CMake:
The NDK build tool recommended by Android has been supported since AS 2.2 (including version 2.2).
2. Environmental Construction
Install the tools needed for NDK development
Install the following components in SDK Tools:
Cmake: NDK Construction Tool
LLDB: NDK Debugging Tool
NDK: NDK Development Toolset
Create NDK project
When creating a project, check the Include C++ support option, and then go all the way to the Customize C++ Support settings page:
You can see three options:
C++ Standard: C++ Standard. Selecting Toolchain Default will use the default Make configuration.
Exceptions Support: Supports C++ exception handling, marked - fexceptions.
Runtime Type Information Support: Supports runtime type recognition, marked as
- frtti, which allows programs to use pointers or references to base classes to check the actual derived types of the pointers or references to the objects they refer to.
Here we use the default C++ standard, do not check the following two options, click the [Finish] button to enter the next link.
3.NDK project
Look at the project catalogue:
In the figure above, the difference between NDK project and common project is marked with red boxes. Let's take a look at the following:
First, let's look at the build.gradle configuration:
apply plugin: 'com.android.application' android { compileSdkVersion 26 defaultConfig { applicationId "com.yl.ndkdemo" minSdkVersion 15 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" externalNativeBuild { cmake { cppFlags "" } } } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } externalNativeBuild { cmake { path "CMakeLists.txt" } } } dependencies { // Reference to some libraries }
You can see two more externalNativeBuild configuration items in the build.gradle configuration:
-
In defaultConfig:
Mainly configure the command parameters of Cmake, for example, when creating a project, if the Exceptions Support and Runtime Type Information Support options are checked, the configuration is as follows:
externalNativeBuild { cmake { cppFlags "-fexceptions -frtti" } }
More command parameters can be viewed Android NDK CMake document
-
Outside defaultConfig:
This paper mainly defines the path of CMake construction script CMakeLists.txt.
CMake build script CMakeLists.txt
CMakeLists.txt is the construction script of CMake, which is equivalent to Android.mk in ndk-build. Look at CMakeLists.txt:
# Setting the Minimum Version of Cmake cmake_minimum_required(VERSION 3.4.1) # Compile library add_library( # Set the library name native-lib # Setting up the library mode # SHARED mode compiles so files, STATIC mode does not. SHARED # Setting Native Code Path src/main/cpp/native-lib.cpp ) # Location library find_library( # The name of library log-lib # Store the library path as a variable that can be used elsewhere to reference the NDK Library # Set the variable name here log ) # Associated library target_link_libraries( # Associated library native-lib # Associate native-lib with log-lib ${log-lib} )
This is a basic CMake build script. For more script configurations, please refer to CMAKE manual I can't understand it! That's OK. Here's the Chinese version. CMAKE Manual - Chinese Version.
Native code native-lib.cpp
Android provides a simple JNI interactive Demo that returns a string to the Java layer. The method name is named by Java_package name_class name_method name:
#include <jni.h> #include <string> extern "C" JNIEXPORT jstring JNICALL Java_com_yl_ndkdemo_MainActivity_stringFromJNI( JNIEnv *env, jobject /* this */) { std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str()); }
See how to call:
public class MainActivity extends AppCompatActivity { // Load native-lib without lib prefix static { System.loadLibrary("native-lib"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // Display the acquired string on TextView TextView tv = (TextView) findViewById(R.id.sample_text); tv.setText(stringFromJNI()); } /** * native-lib Native method in */ public native String stringFromJNI(); }
The invocation method is very simple, the code has been written comments, see the effect:
Generate so file
In CMakeLists.txt, the compilation mode of library is set to SHARED mode. Clicking on the compilation button of AS, the so files corresponding to different CPU architectures will be generated in the app > build > intermediates > cmake > debug > obj directory:
The generated so files can also be used in other projects. Create a jniLibs folder under the app > SRC > main directory of the project, copy the generated so files (with CPU architecture directory) into the jniLibs folder, and use them normally according to the call method mentioned above.
Configure abiFilters in app build.gradle to output so files for the specified ABI:
defaultConfig { ... ndk { abiFilters "armeabi", "armeabi-v7a", "arm64-v8a", "x86", "x86_64", "mips", "mips64" } }
4. Write at the end
More NDK development articles will be updated in the future, please look forward to it!