原生开发套件 (NDK) 是一套工具,使能够在 Android 应用中使用 C 和 C++ 代码,并提供众多平台库,可使用这些平台库管理原生 activity 和访问实体设备组件,例如传感器和触控输入。
NDK 可能不适合大多数 Android 编程初学者,这些初学者只需使用 Java 代码和框架 API 开发应用。
如果需要实现下列目标,NDK 就能派上用场:
- 进一步提升设备性能,以降低延迟或运行游戏或物理模拟等计算密集型应用。
 - 重复使用自己或其他开发者的 C 或 C++ 库。
 
开发者可以在 Android Studio 2.2 或更高版本中使用 NDK 将 C 和 C++ 代码编译到原生库中,然后使用 Android Studio 的集成构建系统 Gradle 将原生库打包到 APK 中。Java 代码随后可以通过 Java 原生接口 (JNI) 框架调用原生库中的函数。
Android Studio 编译原生库的默认构建工具是 CMake。由于很多现有项目都使用 ndk-build 构建工具包,因此 Android Studio 也支持 ndk-build。如果创建新的原生库,则应使用 CMake。
一、基本流程操作:
Android Studio 设置完成后,可以直接创建支持 C/C++ 的新项目。但如果需要向现有 Android Studio 项目添加或导入原生代码,可以按以下基本流程操作:
-  
编写 C 代码:首先,你需要编写 C 代码,并将其编译成适用于 Android 平台的共享库(
.so文件)。这通常需要使用 Android NDK(Native Development Kit),它提供了用于编译本地代码的工具链。 -  
创建 Android 项目:接下来,需要创建一个 Android 项目,用于包装你的 C 代码和 Java/Kotlin 代码。这个项目可以使用 Android Studio 来创建和管理。
 -  
集成本地库:在 Android 项目中,需要将编译好的
.so文件放置在正确的位置,通常是在app/src/main/jniLibs/<ABI>/目录下,其中<ABI>是目标设备的 ABI(如armeabi-v7a,arm64-v8a,x86,x86_64等)。这样,Android 运行时就能找到并加载这些本地库。 -  
使用 JNI 调用 C 函数:在 Java 或 Kotlin 代码中,可以使用 JNI(Java Native Interface)来调用 C 函数。需要声明本地方法,并在 C 代码中实现这些方法的逻辑。JNI 允许 Java/Kotlin 代码与本地代码进行交互。
 -  
构建和测试:最后,构建你的 Android 应用,并在目标设备上进行测试。确保你的 C 代码能够正确执行,并且与 Java/Kotlin 代码之间的交互没有问题。
 
二、示例代码:
Android 提供了 Java Native Interface (JNI) 来调用 native 代码(如 C/C++)。下面是一个简单的示例,帮你了解如何在 Android App 里调用 C 代码。
C 代码 (fir.c)
#include <stdio.h>
void fir(int* input, int* output, int length) {
    for (int i = 0; i < length; i++) {
        output[i] = input[i] * 2; // 一个简单的 FIR 滤波器
    }
}
 
这个 C 代码定义了一个 fir 函数,它将输入数组乘以 2,并将结果存储在输出数组中。
JNI 头文件 (fir.h)
#ifndef FIR_H
#define FIR_H
#ifdef __cplusplus
extern "C" {
#endif
void Java_MainActivity_fir(JNIEnv* env, jobject thiz, jintArray input, jintArray output, jint length);
#ifdef __cplusplus
}
#endif
#endif  // FIR_H
 
这个头文件定义了一个 JNI 函数 Java_MainActivity_fir,它将被 Java 代码调用。该函数将输入数组、输出数组和长度作为参数。
JNI 实现文件 (fir.cpp)
#include "fir.h"
#include "jni.h"
void Java_MainActivity_fir(JNIEnv* env, jobject thiz, jintArray input, jintArray output, jint length) {
    // 获取输入数组的指针
    jint* input_ptr = env->GetIntArrayElements(input, NULL);
    // 获取输出数组的指针
    jint* output_ptr = env->GetIntArrayElements(output, NULL);
    // 调用 C 函数
    fir(input_ptr, output_ptr, length);
    // 释放数组指针
    env->ReleaseIntArrayElements(input, input_ptr, 0);
    env->ReleaseIntArrayElements(output, output_ptr, 0);
}
 
这个文件实现了 JNI 函数 Java_MainActivity_fir。它获取输入数组和输出数组的指针,调用 C 函数 fir,并释放数组指针。
Android 项目结构
jni目录:包含 C 代码和 JNI 头文件fir.cfir.hfir.cpp
java目录:包含 Java 代码MainActivity.java
Java 代码 (MainActivity.java)
public class MainActivity extends AppCompatActivity {
    // 加载 native 库
    static {
        System.loadLibrary("fir");
    }
    // 声明 native 方法
    public native void fir(int[] input, int[] output, int length);
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 创建输入数组
        int[] input = new int[] {1, 2, 3, 4, 5};
        // 创建输出数组
        int[] output = new int[input.length];
        // 调用 native 方法
        fir(input, output, input.length);
        // 打印输出结果
        for (int i = 0; i < output.length; i++) {
            Log.d("MainActivity", "output[" + i + "] = " + output[i]);
        }
    }
}
 
这个 Java 代码加载 native 库,声明 native 方法 fir,并在 onCreate 方法中调用该方法。
Android.mk 文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE    := fir
LOCAL_SRC_FILES := fir.cpp
LOCAL_LDLIBS    := -llog
include $(BUILD_SHARED_LIBRARY)
 
这个文件告诉 Android NDK 如何编译 native 库。
三、编译和运行:
- 在 Android 项目目录下创建 
jni目录,并将 C 代码和 JNI 头文件添加到该目录下。 - 在 
jni目录下创建Android.mk文件,并添加编译指令。 - 使用 Android NDK 编译 native 库:
ndk-build NDK_DEBUG=1 - 在 Java 代码中加载 native 库,并调用 native 方法。
 - 运行 Android App,并查看输出结果。
 
输出结果应该是:
D/MainActivity: output[0] = 2
 D/MainActivity: output[1] = 4
 D/MainActivity: output[2] = 6
 D/MainActivity: output[3] = 8
 D/MainActivity: output[4] = 10
小结:
上述是基本的概念流程,如果想自己试一试,可以以此Hello JNI代码为例,增加输入和显示等,修改算法,构建一个在手机运行的 app,我做了一个简单的 app,截图如下:

老徐,端午,2024/6/10

















![已解决Error || RuntimeError: size mismatch, m1: [32 x 100], m2: [500 x 10]](https://img-blog.csdnimg.cn/direct/aa86eef0d81e4f279688a772e92ea3bc.webp#pic_center)

