NDK<第一篇>:NDK集成开发流程

一、JNI简介

JNI = Java Native Interface,Java本地接口。

二、交叉编译

在什么平台运行的软件就需要在什么平台进行编译,为什么Android可以在Windows下kaifa-并在手机的unix系统上运行呢?这是因为Java是运行在虚拟机上的。为什么说Java是一次编译到处执行?

交叉编译:在一个平台下编译出另一个平台下可以运行的本地代码

  • cpu平台 x86 arm
  • 操作系统平台 windows linux mac os unix
  • 不同的cpu和不同的操作系统所支持的指令集是不一样的,所以在哪个平台上的软件需要在哪个平台上进行编译。

三、NDK简介

NDK = native develop kit,本地开发工具集。
通过NDK来实现交叉编译,NDK是Google提供的。

四、NDK集成开发流程(以cmake工程为例)

1、下载NDK
解压NDK的zip包到非中文目录

2、配置环境变量
根目录中有ndk-build.cmd文件,将根目录配置环境变量path中

3、在local.properties 中配置ndk根路径
ndk.dir=C:\Users\94317\AppData\Local\Android\ndk\android-ndk-r25b

4、在gradle.properties 中配置useDeprecatedNdk 属性
android.useDeprecatedNdk = true,作用是兼容老的ndk
目前,useDeprecatedNdk 已经不支持,改为cmake或者ndk-bundle

5、定义本地接口

public class JNI {

    /**
     * 定义本地接口
     */
    public native String stringFromJNI();

}

6、编写C++代码

在main目录下创建一个任意名称的文件夹,比如cpp文件夹,在cpp文件夹中创建一个cpp文件,比如:native-lib.cpp,代码如下:

#include 
#include 
using namespace std;

extern "C" JNIEXPORT jstring JNICALL
Java_com_nobug_jniproject_JNI_stringFromJNI(JNIEnv* env,jobject /* this */) {
    string hello = "Hello from C++";
    return env->NewStringUTF(hello.c_str());
}

extern "C":在编译时,模式是以C++11标准编译,不支持C语言的编译,所以需要添加 extern "C",能够让C和C++互相调用。
jstring:表示返回值是string类型

7、cmake配置

在cpp目录下新建cmake文件,文件名固定,不可修改:CMakeLists.txt,配置如下:

# 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.18.1)

# Declares and names the project.

project("jniproject")

# 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.
        jniproject

        # 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.
        jniproject

        # Links the target library to the log library
        # included in the NDK.
        ${log-lib})

cmake_minimum_required:指定cmake的最小版本
project("jniproject"):cmake工程名称
add_library:添加库。jniproject:库名,SHARED:表示动态库

8、模块的build.gradle 下配置cmake

android {
    compileSdk 32

    defaultConfig {
        // ...
        externalNativeBuild {
            cmake {
                abiFilters 'armeabi-v7a'
                cppFlags "-std=c++11 -frtti -fexceptions -Os -Wall"
            }
        }
        ndk { // "armeabi-v7a", "arm64-v8a"
            abiFilters 'armeabi-v7a'
        }
    }

    externalNativeBuild {
        cmake {
            path file('src/main/cpp/CMakeLists.txt')
            version '3.18.1'
        }
    }

}

cmake 指定c++11标准,并且cpu架构需要指定为 armeabi-v7a,如果不指定cpu架构,默认的cpu架构是:arm64-v8a

9、编译

编译之后生成 so 库:

image.png

10、加载动态库

public class JNI {

    static {
        // 导入动态库
        System.loadLibrary("jniproject");
    }

    ...

}

11、链接动态库

public class MainActivity extends AppCompatActivity {

    private ActivityMainBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        // Example of a call to a native method
        TextView tv = binding.sampleText;
        tv.setText(new JNI().stringFromJNI());
    }
}

五、生成头文件

image.png

头文件代码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
/* Header for class com_nobug_jniproject_JNI */

#ifndef _Included_com_nobug_jniproject_JNI
#define _Included_com_nobug_jniproject_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_nobug_jniproject_JNI
 * Method:    stringFromJNI
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_nobug_jniproject_JNI_stringFromJNI
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

该文件可以帮助程序员快速完成JNI接口的程序编写。
生成该文件只需要一个命令即可生成。

第一步:切换目录

cd .\app\src\main\java\

第二步:拷贝Java本地接口文件的引用

image.png
image.png

复制出来的内容是:com.nobug.jniproject.JNI

第三步:执行命令生成头文件

javah com.nobug.jniproject.JNI

六、快速创建JNI函数

除了使用 javah 生成JNI代码,还可以使用快捷键可以直接生成。

首先定义好native函数,按下快捷键 alt+enter,如下图:

image.png

选择"Create JNI function for xxxx",可以快速生成JNI代码。

[本章完...]

你可能感兴趣的:(NDK<第一篇>:NDK集成开发流程)