再看!NDK交叉编译动态库并在Android中调用

一、前言

前面文章写过了,使用NDK交叉编译C/C++文件为静态库和动态库,在安卓中调用,当时环境在windows上,不知道是不是环境原因导致动态库无法使用,这次电脑环境为纯Ubuntu系统,再来重走一下来时路...

二、编译动态库

方式一(只在Ubuntu上使用)

假设,你的电脑上已经安装了NDK了

查看NDK版本

bash 复制代码
ls ~/Android/Sdk/ndk/

这个是AS默认创建的SDK路径,如果你电脑上有NDK的话,会有以下输出:21.4.7075529 23.1.7779620

1. 选择NDK版本

建议使用较新的

bash 复制代码
export NDK_HOME=~/Android/Sdk/ndk/23.1.7779620  

2. 创建项目目录结构

bash 复制代码
mkdir -p native-lib/jni
cd native-lib

3. 准备源文件

test.c文件放入jni目录,内容如下:

bash 复制代码
 #include <stdio.h>
 #include <android/log.h>

 int test_function() {
     __android_log_print(ANDROID_LOG_INFO, "NativeLib", "Hello from C!");
     return 20250429;
 }

4. 创建编译配置文件

创建jni/Android.mk

bash 复制代码
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE    := testlib
LOCAL_SRC_FILES := test.c
LOCAL_LDLIBS    := -llog

include $(BUILD_SHARED_LIBRARY)

创建jni/Application.mk

bash 复制代码
APP_ABI := armeabi-v7a arm64-v8a 
APP_PLATFORM := android-21

5. 编译动态库

bash 复制代码
$NDK_HOME/ndk-build

编译会有以下内容输出:

bash 复制代码
xaye@orange:~/dev/native-lib$ $NDK_HOME/ndk-build
[armeabi-v7a] Compile thumb  : testlib <= test.c
[armeabi-v7a] SharedLibrary  : libtestlib.so
[armeabi-v7a] Install        : libtestlib.so => libs/armeabi-v7a/libtestlib.so
[arm64-v8a] Compile        : testlib <= test.c
[arm64-v8a] SharedLibrary  : libtestlib.so
[arm64-v8a] Install        : libtestlib.so => libs/arm64-v8a/libtestlib.so

此时的目录结构为:

bash 复制代码
libs/
├── arm64-v8a/
│   └── libtestlib.so
├── armeabi-v7a/
│   └── libtestlib.so
obj/ (中间文件)

方式一,编译动态库结束!

方式二(Ubuntu和Windows上都可)

在安卓项目中编译

项目目录结构通常如下

bash 复制代码
你的Android项目/
├── app/
│   ├── src/
│   │   ├── main/
│   │   │   ├── java/        # Java代码
│   │   │   ├── cpp/         # C/C++代码(可选)
│   │   │   └── jniLibs/     # 预编译的.so文件(如果不用CMake直接编译)
│   ├── build.gradle         # 模块配置
│   └── CMakeLists.txt       # (推荐位置)CMake配置文件
└── ...
  • app/目录下创建CMakeLists.txt(如果不存在)

  • 将你的test.c文件放在app/src/main/cpp/目录下

  • 修改app/build.gradle

bash 复制代码
android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                cppFlags ""
                // 指定ABI(可选)
                abiFilters "arm64-v8a", "armeabi-v7a"
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}
  • CMakeLists.txt内容:
bash 复制代码
cmake_minimum_required(VERSION 3.4.1)
add_library(testlib SHARED src/main/cpp/test.c)
find_library(log-lib log)
target_link_libraries(testlib ${log-lib})

然后编译项目 生成debug apk,在下面目录就可以找到生成的动态库

方式二,编译动态库结束!

三、验证动态库在安卓项目中使用

写一个 .cpp(JNI桥接层),在 .cppextern "C" 引用 .so 里面的函数,然后正常JNI导出给Java调用!

将编译好的动态库,放到如下目录中:

bash 复制代码
app/src/main/jniLibs/arm64-v8a/libtestlib.so
app/src/main/jniLibs/armeabi-v7a/libtestlib.so

在cpp 目录下,创建 native_bridge.cpp 文件,内容如下:

bash 复制代码
#include <jni.h>

extern "C"
// 声明你.so里暴露的C函数
int test_function();


extern "C"
JNIEXPORT jint JNICALL
Java_com_xaye_testcompiler_NativeLib_test_1function(JNIEnv *env, jclass thiz) {
    // 调用.so里的原生函数
    return test_function();
}
  • 这里 Java_com_xaye_testcompiler_NativeLib_test_1function 是标准 JNI 规范。

  • 写一个JNI包装方法,在里面调用 .so 里的 test_function()

配置 CMakeLists.txt 来链接你的 .so

在你的 CMakeLists.txt 里:

bash 复制代码
cmake_minimum_required(VERSION 3.4.1)

# 指向你的 .so 和头文件
add_library(testlib SHARED IMPORTED)
set_target_properties(testlib PROPERTIES IMPORTED_LOCATION
        ${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libtestlib.so
)

# 编译自己的 native_bridge.cpp
add_library(native-bridge SHARED native_bridge.cpp)

# 链接你的 native-bridge 依赖 testlib.so
target_link_libraries(native-bridge testlib log)
  • testlib.so放在 app/src/main/jniLibs/armeabi-v7a/arm64-v8a/ 下。

  • native-bridge 是你自己编译出来的新 .so,Android系统加载的是你这层。

  • 修改app/build.gradle

bash 复制代码
android {
    defaultConfig {
        externalNativeBuild {
            cmake {
                cppFlags ""
                // 指定ABI(可选)
                abiFilters "arm64-v8a", "armeabi-v7a"
            }
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
        }
    }
}

注意,CMakeLists.txt 放在了src/main/cpp/下,这里的路径要和实际的一样!

Java 层使用方式:

java 复制代码
package com.xaye.testcompiler;

public class NativeLib {
    static {
        System.loadLibrary("native-bridge"); // 只加载你编译的这个桥接so!
    }

    public static native int test_function();
}

调用:

java 复制代码
int result = NativeLib.test_function();
Log.d("TEST", "C 函数返回值: " + result); 

结果:

最后

回头看下,也没有那么复杂,大致流程就是正确编译出动态库,由于安卓调用C/C++代码,需要JNI,所以再写个.cpp文件,链接动态库和安卓代码,这个桥接作用的.cpp文件,就是使用JNI技术,桥接代码就是CMakeLists.txt,结束!

相关推荐
ALex_zry1 小时前
程序运行报错分析文档
android·c++
投笔丶从戎2 小时前
Kotlin Multiplatform--04:经验总结(持续更新)
android·开发语言·kotlin
stevenzqzq2 小时前
kotlin flow的写法
android·kotlin·flow
悠哉清闲2 小时前
Kotlin 协程 (三)
android·开发语言·kotlin
androidwork2 小时前
在 Android 中实现支持多手势交互的自定义 View(Kotlin 完整指南)
android·kotlin·交互
悠哉清闲2 小时前
Kotlin 协程 (二)
android·开发语言·kotlin
androidwork2 小时前
使用 ARCore 和 Kotlin 开发 Android 增强现实应用入门指南
android·kotlin
Ryannn_NN4 小时前
avalonia android连接模拟器时报错adb cannot run as root in production builds,没有权限
android·adb·wpf·xamarin
ii_best4 小时前
按键精灵ios/安卓辅助工具高级函数OcrEx文字识别(增强版)脚本开发介绍
android·ios
_龙小鱼_4 小时前
Android日活(DAU)检测的四大实现方案详解
android