Android JNI from 0 to 1 Getting Started Tutorial (2)

After the introduction of the previous " Android JNI from 0 to 1 Getting Started Tutorial (1) ", we have a preliminary understanding of JNI. Next, I will introduce how to build native libraries from the ndk-build method and the cmake method:

This article uses the development environment:

Android Studio 4.0+

compileSDK 33

gradle 7.2.1

1. ndk-build

ndk-build depends on the configuration file Android.mk, and the location where the code is stored is usually the jni directory

1. Create a new java test class

package com.example.jni;

public class JNIDemo {

    static {
        //这个库名必须跟Android.mk的LOCAL_MODULE对应
        System.loadLibrary("JniDemo");
    }

    public static native String test();
}

2. Create the jni directory

3. Generate .h file

Open the command line, switch to the java directory of the project, and use the following command to generate the .h header file. (If there is no javah command, please configure the jdk environment variable first)

javah -d ../jni com.example.jni.JNIDemo

The generated files will appear in the jni directory

 4. Implement specific C++ functions

Create a new .cpp file in the jni directory with the following content

#include "com_example_jni_JNIDemo.h" //引入刚刚生成的头文件

extern "C"
//实现test函数实际功能
JNIEXPORT jstring JNICALL Java_com_example_jni_JNIDemo_test(JNIEnv * env, jclass clz){
    return env->NewStringUTF("hello world");
}

 5. Configure Android.mk and Application.mk

Create these two files in the jni directory. The contents of the files are as follows:

Android.mk:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#定义要构建生成的本地库的名称,以下示例会生成名为 libJniDemo.so 的库。
LOCAL_MODULE := JniDemo

#指定源代码文件
LOCAL_SRC_FILES := \
JNITest.cpp \

include $(BUILD_SHARED_LIBRARY)

 Application.mk

#指定用于此应用的 C++ 标准库,默认情况下使用 system STL。其他选项包括 c++_shared、c++_static 和 none
APP_STL := c++_shared
APP_CPPFLAGS := -frtti -fexceptions
# APP_ABI:指定生成哪些cpu类型的so, all代表全部 常用的有:armeabi-v7a,arm64-v8a,x86,x86_64
APP_ABI := armeabi-v7a arm64-v8a
#APP_PLATFORM 会声明构建此应用所面向的 Android API 级别,并对应于应用的 minSdkVersion
APP_PLATFORM := android-21

Note : Gradle  externalNativeBuild will ignore  APP_ABI. Please use   blocks or chunks inside ndk blocks  (see configuration below).abiFiltersabi

6. Generate .so file

You can directly use the ndk-build command to build, or you can configure a shortcut in AndroidStudio. (This example uses ndk version 21.4.7075529)

(1) Build with the build command
Switch the console to the jni directory, that is, the directory containing Android.mk and Application.mk, execute the ndk-build command, and it can be found in the libs folder after success. (If there is no ndk-build command, you need to configure the ndk environment variable first)

(2) Configure shortcut tools

Open File-Settings-Tools-External Tools, add a new tool, name it ndk-build (name it at will), and select ndk-build.cmd in the directory where your ndk is located in the Program configuration, which is usually installed in your AndroidStudio The ndk directory under the directory, the Working directory fills in the jni directory of the project

Click to execute ndk-build to generate the .so file

 7. Use native methods in code

public class MainActivity extends AppCompatActivity {

    private TextView tvMsg;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvMsg = findViewById(R.id.tv_msg);
        //调用native方法
        String msg = JNIDemo.test();
        tvMsg.setText(msg);
    }
}

If you are using an older version of Gradle, you need to add the following configuration in the build.gradle of the app module to link the so library (the generated so library needs to be placed in the libs directory):

apply plugin: 'com.android.application'

android {
    compileSdkVersion 28
   
    //...其他省略

    sourceSets {
        main {
            jniLibs.srcDirs = ['libs']
        }
    }
}

dependencies {

    //...其他省略
}

 2. CMake

The above describes ndk-build, now let’s talk about how to configure CMake

1. Add the CMakeLists.txt file

Create a new CMakeLists.txt file in the root directory of the app module of the project, and fill in the following content:

#指定CMake的最低版本要求
cmake_minimum_required(VERSION 3.18.1)

# 定义本地库的名称
set(my_lib_name JniDemo)

#添加库配置,如果有多个库,可以添加多个add_library方法
add_library( # 指定生成库的名称,使用上面定义的变量
        ${my_lib_name}
        # 标识是静态库还是动态库 STATIC:静态库 SHARED:动态库
        SHARED
        # C/C++源代码文件路径
        src/main/cpp/JNITest.cpp)

#指定.h头文件的目录
include_directories(src/main/cpp/)

# 指定构建输出路径
set_target_properties(${my_lib_name} PROPERTIES
        LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}")

# 链接外部静态库(如果你的静态库或动态库依赖于其他库或依赖项)
target_link_libraries(MyStaticLibrary PRIVATE MyExternalStaticLibrary)

The functions in the examples add_libraryare used to specify the native library to build and its source code files. target_link_librariesThe function is used to specify the system library to be linked, such as logthe library, which is used to use Android's logging function in native code.

Finally, through set_target_propertiesthe function, you can specify the output path of the generated shared library and place it in libs/${ANDROID_ABI}the directory, which ${ANDROID_ABI}is a CMake variable that represents the ABI (Application Binary Interface) of the current target platform

For more configuration items of CMakeLists.txt, please refer to Configuring Cmake

2. Add externalNativeBuild and ndk configuration in build.gradle under the app module

android {
    compileSdk 33

    defaultConfig {
        applicationId "com.example.jni"
        minSdk 21
        targetSdk 32
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

        ndk {
            //指定编译的ABI平台
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }

    externalNativeBuild {
        cmake {
            //指定CMakeLists文件所在位置
            path "CMakeLists.txt"
        }
    }
  //...省略
}

3. Add C++ files

CMake usually uses the cpp directory instead of the jni directory, so create a new cpp directory under the src/main directory, and then put the .h and .cpp files into it

 4. Generate .so file

Use the Rebuild Project of AS to build the project, that is, the corresponding so file can be generated in the libs directory.

 Well, so far the introduction of the two methods has been finished.

For more information about the configuration, please refer to Google's official documentation.

postscript

In the above process, we involved two concepts: dynamic library and static library

During the NDK compilation process, these are two different library file generation methods.

1. Dynamic Library (Dynamic Library):

  • A dynamic library is a library file that is loaded and linked into an application at runtime. In Android NDK, the file extension of dynamic library is usually  .so(Shared Object).
  • Dynamic libraries can be shared by multiple applications or modules to reduce disk space and memory usage. Multiple applications can load and use the same dynamic library at the same time.
  • The loading and linking of the dynamic library is carried out at runtime, which means that the library files can be dynamically loaded and replaced during the running of the application, thereby realizing the convenience of updating and maintenance.
  • In Android NDK, dynamic libraries are generated using C/C++ compilers, and  ndk-build compiled and built using build tools such as CMake or CMake.

2. Static Library:

  • A static library is a library file that is linked into an application at compile time. In the Android NDK, the file extension of the static library is usually  .a(Archive).
  • Static libraries are copied into the executable when the application is compiled, and every application that uses a static library includes a complete copy of the library.
  • The advantage of static libraries is that they can provide independent executable files without relying on external library files. It may be slightly better than a dynamic library in performance, because the function call of the static library is direct, without the dynamic link process.
  • In Android NDK, static libraries are also generated using C/C++ compilers, and  ndk-build compiled and built using build tools such as CMake or CMake.

In Android NDK development, you can choose to use dynamic libraries or static libraries according to your needs. Dynamic libraries are suitable for scenarios where multiple applications share library files for easy update and maintenance, while static libraries are suitable for scenarios that require independent executable files, performance optimization, and do not need to rely on external libraries.

Guess you like

Origin blog.csdn.net/gs12software/article/details/131636204