0
点赞
收藏
分享

微信扫一扫

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

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.7779620

2. 创建项目目录结构

mkdir -p native-lib/jni
cd native-lib

3. 准备源文件

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-21

5. 编译动态库

$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桥接层),在 .cppextern "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,结束!

举报

相关推荐

0 条评论