在 Android App 里使用 C 代码 - NDK

原生开发套件 (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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

保存图片奇怪的bug

今天发现一个奇怪的bug 这个的dpi是100de ,但是我取完切片之后&#xff0c;发现这个结果就变了

Word中插入Mathtype右编号,调整公式与编号的位置

当你已经将mathtype内置于word后&#xff0c;可以使用右编号快速插入公式 但是往往会出现公式和编号出现的位置或之间的距离不合适 比如我在双栏下插入公式&#xff0c;会发现插入的公式与编号是适用于单栏的 解决办法&#xff1a; 开始->样式->MTDisplayLquation -&g…

基于Java+SpringBoot制作一个景区导览小程序

基于Java+SpringBoot制作一个景区导览小程序。其中系统前端功能包括注册登录、景区采风、旅游导览、地图导航、发布采风、门票预订、修改个人信息;系统后台功能包括用户管理、景区管理、采风管理等模块。 摘要一、小程序1. 创建小程序2. 首页3. 景区采风页4. 旅游导览页5. 发布…

让GNSSRTK不再难【第二天-第3部分】

第11讲 定位方程构建以及最小二乘 11.1 定位方程重构 历史讲中我们已经初步构建了单点定位的先验残差&#xff1a; p i s P i s − ( X s − X 0 ) 2 ( Y s − Y 0 ) 2 ( Z s − Z 0 ) 2 c δ t r − I i s − T i s − ϵ P i s p_i^s P_i^s - \sqrt{(X^s - X_0)^2 (Y…

学生信息管理(C语言)

学生信息管理 (1)问题描述 学生信息包括:学号,姓名,年龄,性别,出生年月,地址,电话,E-mail等。试设计一学生信息管理系统,使之能提供以下功能: 系统以菜单方式工作学生信息录入功能(学生信息用文件保存)---输入学生信息浏览功能---输出查询、排序功能---算法1、…

2024全国大学生数学建模竞赛优秀参考资料分享

0、竞赛资料 优秀的资料必不可少&#xff0c;优秀论文是学习的关键&#xff0c;视频学习也非常重要&#xff0c;如有需要请点击下方名片获取。 一、赛事介绍 全国大学生数学建模竞赛(以下简称竞赛)是中国工业与应用数学学会主办的面向全国大学生的群众性科技活动&#xff0c;旨…

【C语言】C语言—通讯录管理系统(源码)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

Go singlefight 源码详解|图解

写在前面 通俗的来说就是 singleflight 将相同的并发请求合并成一个请求&#xff0c;进而减少对下层服务的压力&#xff0c;通常用于解决缓存击穿的问题。 详解 基础结构 golang.org/x/sync/singleflight singleflight结构体&#xff1a; type call struct {wg sync.WaitGro…

【recast-navigation-js】使用three.js辅助绘制Agent寻路路径

目录 说在前面setAgentTarget绘制寻路路径结果问题其他 说在前面 操作系统&#xff1a;windows 11浏览器&#xff1a;edge版本 124.0.2478.97recast-navigation-js版本&#xff1a;0.29.0golang版本&#xff1a;1.21.5上一篇&#xff1a;【recast-navigation-js】使用three.js辅…

【代码+详解】算法题 : 金银岛

❗❗❗必看: 下列题我全部都使用 Java 语言写的,并且均可以提交成功,获得Accepted 结果的. 如果代码和详解看了之后,对答案有任何疑问,都可以在评论区提出来,我都会一个一个回答. ❗❗❗感谢大家的支持,如果喜欢我的博客,关注 点赞 收藏 评论一波,非常感谢!!! 文章目录 题目:…

AWT常用组件

AWT中常用组件 前言一、基本组件组件名标签(Label类)Label类的构造方法注意要点 按钮(Button)Button的构造方法注意要点 文本框(TextField)TextField类的构造方法注意要点 文本域&#xff08;TextArea&#xff09;TextArea 的构造方法参数scrollbars的静态常量值 复选框&#x…

Java_Map集合

认识Map集合 Map集合称为双列集合&#xff0c;格式&#xff1a;{key1value&#xff0c;key2value2,key3value3,…},一次需要存一对数据作为一个元素。 Map集合的每个元素“Keyvalue” 称为一个键值对/键值对对象/一个Entry对象&#xff0c;Map集合也被叫做“键值对集合” Map集…

攻防演练之-网络集结号

每一次的网络安全攻防演练都是各个安全厂商期待的网络安全盛会&#xff0c;因为目前的安全生态导致了只有在网络安全攻防演练期间&#xff0c;网络安全的价值才会走向台前&#xff0c;收到相关方的重视。虽然每一次都会由于各种原因不能如期举行&#xff0c;但是这一次的推迟总…

Anaconda配置环境

查看存在的环境 conda list创建环境 #创建 名称为python38的python环境 conda create -n python38 python3.8 #激活 conda activate python38 #退出当前环境 conda deactivate安装python包 #安装numpy包 conda install numpy #安装指定版本 conda install numpy1.0.2 #安装指…

内存卡执行格式化数据还能恢复吗?

众所周知&#xff0c;内存卡对各类电子产品的正常使用至关重要。但在日常使用过程中&#xff0c;错误操作可能导致珍贵资料丢失或意外格式化。相较于其它的删除方式&#xff0c;执行格式化导致的相关问题&#xff0c;想要去恢复的话难度是很高的。那么&#xff0c;内存卡执行格…

Java数组的定义 ,基本概念与使用

数组的定义 1.问题:想将一个数据保存起来,我们可以使用变量,但是变量一次只能存储一个数据,所以我们想能不能一次存多个数据2.数组概述:是一个容器,数组本身属于引用数据类型3.作用:一次存储多个数据4.特点:a.既可以存储基本类型的数据,还能存储引用类型的数据b.定长(定义数组…

wx小程序自定义tabbar

1.在app.json文件中&#xff0c;添加自定义tabbar配置&#xff1a;"custom": true "tabBar": {"custom": true,"backgroundColor": "#fafafa","borderStyle": "white","selectedColor": &quo…

k8s——pod控制器

一、pod控制器定义 Pod控制器&#xff0c;又称之为工作负载&#xff08;workload&#xff09;&#xff0c;是用于实现管理pod的中间层&#xff0c;确保pod资源符合预期的状态&#xff0c;pod的资源出现故障时&#xff0c;会尝试进行重启&#xff0c;当根据重启策略无效&#xf…

字符串常量简单介绍

C/C内存四区介绍 如前文所示&#xff0c;字符串常量存储在静态存储区的字符串常量区&#xff0c;这样做的好处是 当程序使用到多个相同的字符串常量时&#xff0c;实际上都是使用的同一份&#xff0c;这样就可以减小程序的体积。注意字符串常量是只读的不能被修改。 如图所示&…

Simscape Multibody与RigidBodyTree:机器人建模

RigidBodyTree&#xff1a;主要用于表示机器人刚体结构的动力学模型&#xff0c;重点关注机器人的几何结构、质量和力矩&#xff0c;以及它们如何随时间变化。它通常用于计算机器人的运动和受力情况。Simscape Multibody&#xff1a;作为Simscape的一个子模块&#xff0c;专门用…