androidNDK 交叉編譯+靜態編譯和動態編譯 .a和 .so庫的引用

我是在 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<stdio.h>
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 文件

在這裏插入圖片描述

  • 導出 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
    在這裏插入圖片描述
  • 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 <jni.h>
#include <string>
#include <android/log.h>
// 用 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");
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章