theme: cyanosis
一、前言
前面文章写过了,使用NDK交叉编译C/C++文件为静态库和动态库,在安卓中调用,当时环境在windows上,不知道是不是环境原因导致动态库无法使用,这次电脑环境为纯Ubuntu系统,再来重走一下来时路...
二、编译动态库
方式一(只在Ubuntu上使用)
假设,你的电脑上已经安装了NDK了
查看NDK版本
ls ~/Android/Sdk/ndk/这个是AS默认创建的SDK路径,如果你电脑上有NDK的话,会有以下输出:21.4.7075529 23.1.7779620
1. 选择NDK版本
建议使用较新的
export NDK_HOME=~/Android/Sdk/ndk/23.1.77796202. 创建项目目录结构
mkdir -p native-lib/jni
cd native-lib3. 准备源文件
将test.c文件放入jni目录,内容如下:
#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
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
APP_ABI := armeabi-v7a arm64-v8a
APP_PLATFORM := android-215. 编译动态库
$NDK_HOME/ndk-build编译会有以下内容输出:
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此时的目录结构为:
libs/
├── arm64-v8a/
│ └── libtestlib.so
├── armeabi-v7a/
│ └── libtestlib.so
obj/ (中间文件)方式一,编译动态库结束!
方式二(Ubuntu和Windows上都可)
在安卓项目中编译
项目目录结构通常如下:
你的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:
android {
defaultConfig {
externalNativeBuild {
cmake {
cppFlags ""
// 指定ABI(可选)
abiFilters "arm64-v8a", "armeabi-v7a"
}
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
}CMakeLists.txt内容:
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桥接层),在 .cpp 中 extern "C" 引用 .so 里面的函数,然后正常JNI导出给Java调用!
将编译好的动态库,放到如下目录中:
app/src/main/jniLibs/arm64-v8a/libtestlib.so
app/src/main/jniLibs/armeabi-v7a/libtestlib.so在cpp 目录下,创建 native_bridge.cpp 文件,内容如下:
#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 里:
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:
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 层使用方式:
package com.xaye.testcompiler;
public class NativeLib {
static {
System.loadLibrary("native-bridge"); // 只加载你编译的这个桥接so!
}
public static native int test_function();
}调用:
int result = NativeLib.test_function();
Log.d("TEST", "C 函数返回值: " + result);结果:
最后
回头看下,也没有那么复杂,大致流程就是正确编译出动态库,由于安卓调用C/C++代码,需要JNI,所以再写个.cpp文件,链接动态库和安卓代码,这个桥接作用的.cpp文件,就是使用JNI技术,桥接代码就是CMakeLists.txt,结束!










