再看!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,结束!

相关推荐
花王江不语15 分钟前
android studio 配置硬件加速 haxm
android·ide·android studio
江太翁2 小时前
mediapipe流水线分析 三
android·mediapipe
与火星的孩子对话3 小时前
Unity进阶课程【六】Android、ios、Pad 终端设备打包局域网IP调试、USB调试、性能检测、控制台打印日志等、C#
android·unity·ios·c#·ip
tmacfrank4 小时前
Android 网络全栈攻略(四)—— TCPIP 协议族与 HTTPS 协议
android·网络·https
fundroid5 小时前
Kotlin 协程:Channel 与 Flow 深度对比及 Channel 使用指南
android·kotlin·协程
草字5 小时前
cocos 打包安卓
android
DeBuggggggg6 小时前
centos 7.6安装mysql8
android
浩浩测试一下7 小时前
渗透信息收集- Web应用漏洞与指纹信息收集以及情报收集
android·前端·安全·web安全·网络安全·安全架构
移动开发者1号8 小时前
深入理解原子类与CAS无锁编程:原理、实战与优化
android·kotlin
陈卓4108 小时前
MySQL-主从复制&分库分表
android·mysql·adb