Android NDK开发入门2之适应idm环境

环境搭建

Android NDK开发实战之环境搭建篇(so库,Gemini ai)-CSDN博客

初始配置

前面已经运行了一个简单的初始程序,现在我们来往初始程序添加类和函数,并成功运行的实验。

一级配置

第一层配置主要是cmake文件环境和一些编译选项。

build配置 可参考:

#build配置
        externalNativeBuild {
            cmake {
                path file('src/main/cpp/CMakeLists.txt')
                version '3.22.1'
                //cpp 编译选项
                cppFlags '-fexceptions -std=c++11'
                //设置c++stl为动态库 默认是静态库  
                arguments '-DANDROID_STL=c++_shared'
            }
        }

二级cmake配置

主要配置生成的动态库名称,具体看注释

为了测试 我们创建一个hello类

#include "iostream"
class hello {
public:
    std::string getVersion();
};


#include "hello.h"
std::string hello::getVersion(){
    return "v1.0 hello word";
}

#include <jni.h>
#include <string>

#include "hello.h"
extern "C" JNIEXPORT jstring JNICALL
//stringFromJNI是c++function名称
Java_com_example_first_1ndk_1cpp_MainActivity_stringFromJNI(
        JNIEnv* env,
        jobject /* this */) {
    std::string helo = "Hello from  first native C++ ";

    hello h;
    helo += h.getVersion();
    return env->NewStringUTF(helo.data());
}

更改cmake,把cpp文件加入

#cmake最低版本
cmake_minimum_required(VERSION 3.22.1)

#最终项目名称 通过${CMAKE_PROJECT_NAME}获得
project("first_ndk_cpp")

add_library(
        #生成的动态库名称
        ${CMAKE_PROJECT_NAME}
        #生成库类型:SHARED动态 默认静态
        SHARED
        #相关cpp文件 假设有全局变量,交叉使用需要注意编译顺序。
        native-lib.cpp
        hello.cpp

)


target_link_libraries(
        #最终生成目标库名称
        ${CMAKE_PROJECT_NAME}
        # 链接其他目标库
        android
        log)

MainActivity

MainActivity 通常被认为是 Android 应用程序的主要入口点。

public class MainActivity extends AppCompatActivity {

    // 静态初始化块, 程序启动时调用
    static {
        //加载名为 first_ndk_cpp 的动态库
        System.loadLibrary("first_ndk_cpp");
    }

    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(stringFromJNI()); //java调用
    }

    /**
     这声明了一个名为 stringFromJNI 的本地方法。该实现位于 first_ndk_cpp 库中。
     */
    public native String stringFromJNI();
}

运行

最后运行输出

小结

熟悉基本配置,知道增加类代码如何重新编译成动态库使用。

动态库实战

场景:编译两份版本动态库 分别输出v1和v2,然后切换运行。

设置动态库导出路劲

前面操作,我们成功编译出动态库。但是路劲很隐蔽,其次规范处理。

实现方法

CMakeLists.txt 设置导出so路劲:

CMAKE_ARCHIVE_OUTPUT_DIRECTORY :默认存放静态库的⽂件夹位置; CMAKE_LIBRARY_OUTPUT_DIRECTORY :默认存放动态库的⽂件夹位置;

cpp 复制代码
# 第一种做法:单独设置动态库的默认输出路径
# 将动态库输出到 `main/jniLibs/${ANDROID_ABI}` 目录
# ${ANDROID_ABI} 是 Gradle 配置的 ABI 策略(如 armeabi-v7a, arm64-v8a 等)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY
    ${CMAKE_CURRENT_SOURCE_DIR}/../libs/jniLibs/${ANDROID_ABI})

# 第二种做法:设置动态库和静态库的默认输出路径
# 将动态库和静态库输出到 `main/jniLibs/${ANDROID_ABI}` 目录
# 注意:LIBRARY_OUTPUT_PATH 是旧版 CMake 的变量,
#推荐使用 CMAKE_LIBRARY_OUTPUT_DIRECTORY 和 CMAKE_ARCHIVE_OUTPUT_DIRECTORY
set(LIBRARY_OUTPUT_PATH
    ${CMAKE_CURRENT_SOURCE_DIR}/../libs/jniLibs/${ANDROID_ABI})

根据这个方法步骤编译两个版本

主要区别:

cpp 复制代码
std::string hello::getVersion(){
    return "v2.0 hello word";
}

尝试版本切换

cpp 复制代码
//定义项目的源代码和资源文件的目录结构
    sourceSets {
        main {
            //指定 JNI 库文件(.so 文件)的存放目录
            jniLibs.srcDirs = ['src/main/libs/jniLibs']
        }
    }

小结

学习如何指定动态库导出路劲,并根据动态库切换,实现多种版本调用。

特定cpu架构实战

上述讨论了如何导出不同的动态库,现在如果只想根据x86架构导出单个so库如何实现?

先看看为什么需要指定cpu

⽣成指定cpu平台对应的so库⽂件

应⽤⼆进制接⼝(ABI),包含的内容:

1、可执⾏⼆进制⽂件的格式以及⽀持的内容类型,⼆进制⽂件如:程序、共享库...

2、可使⽤的CPU指令集

3、运⾏时内存存储和加载的字节顺序

4、应⽤和系统之间传递数据的规范,以及系统调⽤函数时,如何使⽤堆栈、寄存器

5、如何重整C++名称

abi与 指令集

Android 支持多种 CPU 架构,每种架构对应不同的 ABI。以下是常见的 CPU 架构及其对应的 ABI:

CPU 架构 ABI 说明
ARMv5 armeabi 32 位 ARM 架构,已过时,Android NDK r17 已不再支持。
ARMv7 armeabi-v7a 32 位 ARM 架构,支持硬件浮点运算和 NEON 指令集。
ARMv8 arm64-v8a 64 位 ARM 架构,支持 AArch64 指令集。
MIPS mips 32 位 MIPS 架构,已过时,Android NDK r17 已不再支持。
MIPS64 mips64 64 位 MIPS 架构,已过时,Android NDK r17 已不再支持。
x86 x86 32 位 Intel x86 架构,主要用于模拟器和部分低端设备。
x86_64 x86_64 64 位 Intel x86 架构,主要用于模拟器和高性能设备。

ABI 兼容性是指应用程序在不同 CPU 架构上的运行能力。以下是各 ABI 的兼容性说明:

ABI 兼容性说明
armeabi 兼容 ARMv5 和 ARMv7,但不兼容 ARMv8(64 位)。
armeabi-v7a 兼容 ARMv7,但不兼容 ARMv5 和 ARMv8(64 位)。
arm64-v8a 兼容 ARMv8(64 位),但不兼容 ARMv5 和 ARMv7(32 位)。
mips 仅兼容 MIPS 架构,已过时。
mips64 仅兼容 MIPS64 架构,已过时。
x86 兼容 x86 架构,同时兼容 armeabiarmeabi-v7a(通过二进制翻译运行)。
x86_64 兼容 x86_64 架构,同时兼容 arm64-v8a(通过二进制翻译运行)。

x86 和 x86_64 的特殊性

x86 :主要用于模拟器和部分低端设备。通过二进制翻译,可以运行 armeabiarmeabi-v7a 的代码,但性能较低。

x86_64 :主要用于模拟器和高性能设备。通过二进制翻译,可以运行 arm64-v8a 的代码。

ABI 配置

在 Android 项目中,可以通过 Gradle 配置支持的 ABI。

cpp 复制代码
android {
    defaultConfig {
        ndk {
            abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
        }
    }
}

小结

ARM 架构armeabi-v7aarm64-v8a 是当前主流的架构。

x86 架构:主要用于模拟器,通过二进制翻译可以运行 ARM 架构的代码。

ABI 配置 :通过 Gradle 的 abiFilters 指定支持的 ABI。

问题

当遇到不能run,编译没错误信息,则进行sync now即可解决

学习资料分享

0voice · GitHub

相关推荐
豪宇刘1 小时前
JavaScript 延迟加载的方法
开发语言·javascript
Bytebase1 小时前
MySQL 如何赶上 PostgreSQL 的势头?
运维·数据库·dba·开发者·数据库管理·devops
顾尘眠2 小时前
http常用状态码(204,304, 404, 504,502)含义
前端
摇光932 小时前
js迭代器模式
开发语言·javascript·迭代器模式
acegi135792 小时前
MySQL - 子查询和相关子查询详解
数据库·mysql
背太阳的牧羊人2 小时前
使用 SQL 和表格数据进行问答和 RAG(7)—将表格数据(CSV 或 Excel 文件)加载到向量数据库(ChromaDB)中
数据库·sql·langchain·excel
你若安好我便天晴3 小时前
sql--MERGE INTO
数据库·sql
半桶水专家3 小时前
go怎么终止协程的运行
数据库·sql·golang
weixin_438335403 小时前
【更新中】Mysql问题分析
数据库·mysql
王先生技术栈3 小时前
思维导图,Android版本实现
java·前端