在 Android App 里使用 C 代码 - NDK

news2024/12/27 13:38:14

原生开发套件 (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 项目添加或导入原生代码,可以按以下基本流程操作:

  1. 编写 C 代码:首先,你需要编写 C 代码,并将其编译成适用于 Android 平台的共享库(.so 文件)。这通常需要使用 Android NDK(Native Development Kit),它提供了用于编译本地代码的工具链。

  2. 创建 Android 项目:接下来,需要创建一个 Android 项目,用于包装你的 C 代码和 Java/Kotlin 代码。这个项目可以使用 Android Studio 来创建和管理。

  3. 集成本地库:在 Android 项目中,需要将编译好的 .so 文件放置在正确的位置,通常是在 app/src/main/jniLibs/<ABI>/ 目录下,其中 <ABI> 是目标设备的 ABI(如 armeabi-v7a, arm64-v8a, x86, x86_64 等)。这样,Android 运行时就能找到并加载这些本地库。

  4. 使用 JNI 调用 C 函数:在 Java 或 Kotlin 代码中,可以使用 JNI(Java Native Interface)来调用 C 函数。需要声明本地方法,并在 C 代码中实现这些方法的逻辑。JNI 允许 Java/Kotlin 代码与本地代码进行交互。

  5. 构建和测试:最后,构建你的 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.c
    • fir.h
    • fir.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 库。

三、编译和运行:

  1. 在 Android 项目目录下创建 jni 目录,并将 C 代码和 JNI 头文件添加到该目录下。
  2. jni 目录下创建 Android.mk 文件,并添加编译指令。
  3. 使用 Android NDK 编译 native 库:ndk-build NDK_DEBUG=1
  4. 在 Java 代码中加载 native 库,并调用 native 方法。
  5. 运行 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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1809515.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用 Jetpack Compose 实现 Android 偏好设置分类界面

使用 Jetpack Compose 实现 Android 偏好设置分类界面 Jetpack Compose 提供了一种现代且声明式的构建 Android 用户界面的方法&#xff0c;使其非常适合实现偏好设置分类界面。以下是如何实现的逐步指南&#xff1a; 1. 定义数据模型: 首先&#xff0c;定义数据模型来表示…

集成学习模型对比优化—银行业务

1.Data Understanding 2.Data Exploration 3.Data Preparation 4.Training Models 5.Optimization Model 集成学习模型对比优化—银行业务 1.Data Understanding import pandas as pd from matplotlib import pyplot as plt import seaborn as sns df pd.read_csv(&quo…

表的设计与查询

目录 一、表的设计 1.第一范式&#xff08;一对一&#xff09; 定义&#xff1a; 示例&#xff1a; 2.第二范式&#xff08;一对多&#xff09; 定义&#xff1a; 要求&#xff1a; 示例&#xff1a; 3.第三范式&#xff08;多对多&#xff09; 定义&#xff1a; 要求…

Bio-Info每日一题:Rosalind-06-Counting Point Mutations

&#x1f389; 进入生物信息学的世界&#xff0c;与Rosalind一起探索吧&#xff01;&#x1f9ec; Rosalind是一个在线平台&#xff0c;专为学习和实践生物信息学而设计。该平台提供了一系列循序渐进的编程挑战&#xff0c;帮助用户从基础到高级掌握生物信息学知识。无论你是初…

每日算法——归并排序

什么是归并排序 归并排序是一种分治算法。它将数组不断地分成两半&#xff0c;对每一半进行排序&#xff0c;然后再将排序好的两半合并起来。通过不断重复这个过程&#xff0c;最终得到完全排序的数组。 归并排序的注意点&#xff1a; 空间复杂度&#xff1a;归并排序需要额…

javascript动态绑定

介绍 先来看看ai的解释 动态绑定机制是面向对象编程中的一个核心概念&#xff0c;特别是在Java这样的语言中。它允许在运行时根据对象的实际类型来决定调用哪个方法&#xff0c;而不是在编译时。这是多态性的关键特性之一。 在Java中&#xff0c;动态绑定是通过方法调用和方法…

C#——枚举类型详情

枚举类型 枚举类型&#xff08;也可以称为“枚举器”&#xff09;由一组具有独立标识符&#xff08;名称&#xff09;的整数类型常量构成&#xff0c;在 C# 中枚举类型不仅可以在类或结构体的内部声明&#xff0c;也可以在类或结构体的外部声明&#xff0c;默认情况下枚举类型…

ViT:2 理解CLIP

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调或者LLM背后的基础模型新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提…

大模型基础——从零实现一个Transformer(2)

大模型基础——从零实现一个Transformer(1) 一、引言 上一章主要实现了一下Transformer里面的BPE算法和 Embedding模块定义 本章主要讲一下 Transformer里面的位置编码以及多头注意力 二、位置编码 2.1正弦位置编码(Sinusoidal Position Encoding) 其中&#xff1a; pos&…

linux中xterm窗口怎么调整字体大小

需求&#xff1a;打开的xterm窗口字体比较小&#xff0c;怎么才能调整字体大小&#xff0c;打开的大写&#xff1a; 解决方法&#xff1a; 在home目录下搞一个设置文件 .Xresource&#xff0c;里面内容如下 然后把设置文件添加到 .tcshrc 文件中生效 这样重新打开的xterm字…

MySQL数据库(二)和java复习

一.MySQL数据库学习(二) (一).DQL查询数据 DQL&#xff08;Data Query Language&#xff09;是用于从数据库中检索数据的语言。常见的 DQL 语句包括 SELECT、FROM、WHERE、GROUP BY、HAVING 和 ORDER BY 等关键字&#xff0c;用于指定要检索的数据、数据源、过滤条件、分组方…

《编程小白变大神:DjangoBlog带你飞越代码海洋》

还在为你的博客加载速度慢而烦恼&#xff1f;DjangoBlog性能优化大揭秘&#xff0c;让你的网站速度飞跃提升&#xff01;本文将带你深入了解缓存策略、数据库优化、静态文件处理等关键技术&#xff0c;更有Gunicorn和Nginx的黄金搭档&#xff0c;让你的博客部署如虎添翼。无论你…

助力高考,一组彩色的文字

1、获取文本内容 首先&#xff0c;获取每个<div>元素的文本内容&#xff0c;并清空其内部HTML&#xff08;innerHTML ""&#xff09;。 2、创建<span>元素 然后&#xff0c;它遍历文本的每个字符&#xff0c;为每个字符创建一个新的<span>元素…

《python程序语言设计》2018版第5章第36题改造4.17 石头 剪刀 布某一方超过2次就结束。

代码编写记录 2024.05.04 05.36.01version 换一个什么数代替剪子 我先建立一个函数judgement condition 石头3 剪子2 布1 如何构建一个循环进行的架构&#xff0c;是我们最需要的想法 循环以什么条件开始呢 是小于2个还是大于2个。 guess_num random.randint(1, 3) computer…

nginx优化与防盗链【☆☆☆】

目录 一、用户层面的优化 1、隐藏版本号 方法一&#xff1a;修改配置文件 方法二&#xff1a;修改源码文件&#xff0c;重新编译安装 2、修改nginx用户与组 3、配置nginx网页缓存时间 4、nginx的日志切割 5、配置nginx实现连接超时 6、更改nginx运行进程数 7、开启网…

IPv4 子网掩码计算器—python代码实现

今天聊一下&#xff0c;我用python和vscode工具实现一个IPv4计算器的一些思路&#xff0c;以及使用Python编写IPv4计算器一些好处&#xff1f; 首先&#xff0c;一、Python语法简洁易读&#xff0c;便于理解和维护&#xff0c;即使对编程不熟悉的用户也能快速了解代码逻辑。其…

阿里通义千问 Qwen2 大模型开源发布

阿里通义千问 Qwen2 大模型开源发布 Qwen2 系列模型是 Qwen1.5 系列模型的重大升级。该系列包括了五个不同尺寸的预训练和指令微调模型&#xff1a;Qwen2-0.5B、Qwen2-1.5B、Qwen2-7B、Qwen2-57B-A14B 以及 Qwen2-72B。 在中文和英文的基础上&#xff0c;Qwen2 系列的训练数…

已解决Error || RuntimeError: size mismatch, m1: [32 x 100], m2: [500 x 10]

已解决Error || RuntimeError: size mismatch, m1: [32 x 100], m2: [500 x 10] 原创作者&#xff1a; 猫头虎 作者微信号&#xff1a; Libin9iOak 作者公众号&#xff1a; 猫头虎技术团队 更新日期&#xff1a; 2024年6月6日 博主猫头虎的技术世界 &#x1f31f; 欢迎来…

情景题之小明的Linux实习之旅:linux实战练习1(下)【基础命令,权限修改,日志查询,进程管理...】

小明的Linux实习之旅&#xff1a;基础指令练习情景练习题下 前景提要小明是怎么做的场景1&#xff1a;初识Linux&#xff0c;创建目录和文件场景2&#xff1a;权限管理&#xff0c;小明的权限困惑场景3&#xff1a;打包与解压&#xff0c;小明的备份操作场景4&#xff1a;使用G…