androidNDK 交叉编译+静态编译和动态编译 .a和 .so库的引用

文章目录

      • 配置观景变量
      • 新建.h .c 文件并交叉编译出静态库和动态库
      • AS加载使用静态库和动态库
        • Android Studio mk方式加载静态库
        • Android Studio 通过 CMake加载静态库和动态库

我是在 Linux 上编译的方式

配置观景变量

  • 下载好 NDK 我这里学习采用的是 R17版本 gcc 方式编译 后面的版本采用 cLang编译
    配置环境变量。我直接添加到了 .bash_profile 结尾
export NDK_ARM_GCC="/root/ndk/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc"
export PATH=$PATH:$NDK_ARM_GCC

export NDK_ARM_CFIG="--sysroot=/root/ndk/android-ndk-r17c/platforms/android-23/arch-arm -isystem /root/ndk/android-ndk-r17c/sysroot/usr/include -isystem /root/ndk/android-ndk-r17c/sysroot/usr/include/arm-linux-androideabi"
export PATH=$PATH:$NDK_ARM_CFIG

export NDK_ARM_AR="/root/ndk/android-ndk-r17c/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc-ar"
export PATH+$PATH:$NDK_ARM_AR

新建.h .c 文件并交叉编译出静态库和动态库

  • 新建一个 hello.h 如下:
#include
int getNum();
  • 新建 hello.c 方便测试直接返回了一个数字
#include"hello.h"
int getNum(){
	return 12345;
}
  • 通过NDK的 gcc 交叉编译.c文件 输出 hello.o文件
$NDK_ARM_GCC $NDK_ARM_CFIG -fPIC -c hello.c -o hello.o

运行完成以后如下,生成了 hello.o 文件
在这里插入图片描述

  • 编译静态库
$NDK_ARM_AR rcs -o libhello.a hello.o

生成 libhello.a 文件

  • 编译动态库
$NDK_ARM_GCC $NDK_ARM_CFIG -fPIC -shared hello.c -o libhello.so

生成 libhello.so 文件

androidNDK 交叉编译+静态编译和动态编译 .a和 .so库的引用_第1张图片

  • 导出 libhello.a 和 libhello.so 文件 我使用的 sz 导出
sz libhello.a
sz libhello.so

AS加载使用静态库和动态库

Android Studio mk方式加载静态库

  • 这里只是记录copy一下其他人总结的 mk 使用语法,重点还是在于后面的 cmake 编译
# 这里面能够决定编译 Login.c

# 1.源文件在的位置。宏函数 my-dir 返回当前目录(包含 Android.mk 文件本身的目录)的路径。
# LOCAL_PATH 其实就是Android.mk文件本身的目录的路径
LOCAL_PATH := $(call my-dir)

$(info "LOCAL_PATH:======== ${LOCAL_PATH}")

# 2.清理
include $(CLEAR_VARS)

# TODO 预编译库的引入 == 提前编译好的库
LOCAL_MODULE := get

# LOCAL_SRC_FILES := libget.so
LOCAL_SRC_FILES := libget.a

# 预编译共享库的Makeifle脚本
# include $(PREBUILT_SHARED_LIBRARY)

include $(PREBUILT_STATIC_LIBRARY)

#引入其他makefile文件。CLEAR_VARS 变量指向特殊 GNU Makefile,可为您清除许多 LOCAL_XXX 变量
#不会清理 LOCAL_PATH 变量
include $(CLEAR_VARS)
# TODO end

# 3.指定库名字
#存储您要构建的模块的名称 每个模块名称必须唯一,且不含任何空格
#如果模块名称的开头已是 lib,则构建系统不会附加额外的前缀 lib;而是按原样采用模块名称,并添加 .so 扩展名。
LOCAL_MODULE := MyLoginJar

#包含要构建到模块中的 C 和/或 C++ 源文件列表 以空格分开
LOCAL_SRC_FILES := Login.c \
Test.c

# TODO 开始链接进来
# 静态库的链接
LOCAL_STATIC_LIBRARIES := get
# 动态库链接
#LOCAL_SHARED_LIBRARIES := get

# 导入 log
#LOCAL_LDLIBS := -llog
LOCAL_LDLIBS    := -lm -llog

# 4.动态库
#构建动态库BUILD_SHARED_LIBRARY 最后要动态库
include $(BUILD_SHARED_LIBRARY)

Android Studio 通过 CMake加载静态库和动态库

  • 加载静态库

编译期:编译期的时候,把静态库完整全部Copy一份去执行的。如果修改了静态库,需要重新编译。如果内部使用的库,不会给外部提供,采用静态库

app的gradle配置如下:

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.lu.ndk03"
        minSdkVersion 23
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
        externalNativeBuild {
            cmake {
//                cppFlags ""
                // abiFilters 'armeabi-v7a'
            }
            ndk {
                // 打包生成的 APK 文件指挥包含 ARM 指令集的动态库 不加这个我那个手机导入静态库就崩溃
                abiFilters "armeabi-v7a" /*, "arm64-v8a", "x86", "x86_64"*/
            }
        }
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        cmake {
            path "src/main/cpp/CMakeLists.txt"
            version "3.10.2"
        }
    }
}

  • 静态库添加到了cpp文件夹下 libhello.a
    androidNDK 交叉编译+静态编译和动态编译 .a和 .so库的引用_第2张图片
  • CMake 静态库配置如下:

cmake_minimum_required(VERSION 3.4.1)

add_library(
        native-lib
        SHARED
        native-lib.cpp
        libhello.a
)

# 导入静态库  STATIC:静态   IMPORTED:导入
add_library(hello STATIC IMPORTED)
# 开始真正的导入
set_target_properties(hello PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libhello.a)


find_library(
        log-lib

        log)

target_link_libraries(
        native-lib
        ${log-lib}
        # 链接 hello 库
        hello

)
  • 调用 hello.c 之前写的 getNum() 方法 返回 12345
#include 
#include 
#include 
// 用 C 编译的 getNum 所以需要执行需要 C 环境
extern "C" {
//  自己定义的方法 会返回 12345
int getNum();
}

extern "C" JNIEXPORT jstring JNICALL
Java_com_lu_ndk03_MainActivity_stringFromJNI(
        JNIEnv *env,
        jobject /* this */) {
    std::string hello = "Hello from C++";
    // 这里只是打印了一下 log 查看 getNum是否调用成功
    jint result = getNum();
    // 成功打印出 12345
    __android_log_print(ANDROID_LOG_DEBUG, "lu", "hello的结果=%d", result);
    return env->NewStringUTF(hello.c_str());
}
  • 加载动态库

运行的时候,才会去加载,而且只加载一次(System.loadLIbary(xxxx.so)),当加载一次之后,在内存中存在副本,所有使用的地方都是公用的。比如地图的.so库等。

  • 跟静态库加载需要修改 CMake 的配置如下:

cmake_minimum_required(VERSION 3.4.1)

add_library(
        native-lib
        SHARED
        native-lib.cpp
        #        libhello.a
)

# 导入静态库
#add_library(hello STATIC IMPORTED)
# 开始真正的导入
#set_target_properties(hello PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libhello.a)

# CMAKE_ANDROID_ARCH_ABI 代表 armeabi-v7a  src/main
# add 添加自己的动态库
add_library(hello SHARED IMPORTED)
#${PROJECT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libhello.so
set_target_properties(hello PROPERTIES IMPORTED_LOCATION so存储路径/libhello.so)
# find 只能找系统的
find_library(
        log-lib
        log)

target_link_libraries(
        native-lib
        ${log-lib}
        # 链接 hello 库
        hello
)

MainActivity中

 static {
        // 要写在 总库 之前
        System.loadLibrary("hello");
        System.loadLibrary("native-lib");
    }

你可能感兴趣的:(Android,NDK,Android)