Android NDK 編譯 protobuf

最近收到一個(gè)小伙伴的私信,Android平臺(tái)幫忙編譯一個(gè)Native C++項(xiàng)目,這個(gè)項(xiàng)目中用到了Protobuf 和OpenCV兩個(gè)C++庫(kù),其中OpenCV有現(xiàn)成的動(dòng)態(tài)庫(kù).so了,但是Protobuf并沒(méi)有現(xiàn)成的,需要自己拿源碼編譯!廢話不多說(shuō),直接上干貨...

首先,準(zhǔn)備編譯所需要的工具

在 macOS M4(ARM 架構(gòu))上為 Android 交叉編譯 Protobuf (如v3.19.6版本)的 .so 動(dòng)態(tài)庫(kù),需要使用 Android NDK 配置交叉編譯環(huán)境,針對(duì)不同的 Android CPU 架構(gòu)(如 arm64-v8a、armeabi-v7a、x86_64 等)生成對(duì)應(yīng)版本的庫(kù)。

  1. 安裝依賴工具
    確保已安裝基礎(chǔ)編譯工具cmake和版本控制工具git,使用brew安裝即可。

  2. 下載 Android NDK
    交叉編譯 Android 庫(kù)需要 NDK 提供的工具鏈,可以直接去https://developer.android.com/ndk/downloads/older_releases官網(wǎng)下載,也可以在Android Studio的SDK Manager中下載。下載完成解壓到指定目錄,這個(gè)目錄我們稱為NDK_PATH,也可以將它配置環(huán)境變量中

  3. 下載 Protobuf 3.19.6 源碼

    git clone https://github.com/protocolbuffers/protobuf.git
    cd protobuf
    git checkout v3.19.6
    git submodule update --init --recursive  # 拉取子模塊(如 googletest)
    

接著,交叉編譯 Protobuf 到 Android .so(核心步驟)

Protobuf 支持 CMake 交叉編譯,需為每個(gè)目標(biāo)架構(gòu)(如 arm64-v8a、armeabi-v7a)單獨(dú)配置編譯參數(shù)。

  1. 定義編譯參數(shù)(通用配置)
    先設(shè)置通用變量,方便后續(xù)復(fù)用:
# NDK 路徑(替換為你的實(shí)際路徑)
export NDK_PATH=/Users/xxx/Library/Android/sdk/ndk/26.1.10909125

# Protobuf 源碼根目錄(當(dāng)前目錄)
export PROTOBUF_SRC=$(pwd)

# 輸出目錄(所有架構(gòu)的 .so 最終會(huì)放到這里)
export OUTPUT_DIR=$(pwd)/android_build
mkdir -p $OUTPUT_DIR
  1. 針對(duì)不同架構(gòu)編譯(以 arm64-v8a 為例)
    Android 主流架構(gòu)包括:
    arm64-v8a(64 位 ARM,主流新設(shè)備)
    armeabi-v7a(32 位 ARM,老舊設(shè)備)
    x86_64(64 位 x86,模擬器常用)
    以下以 arm64-v8a 為例,其他架構(gòu)僅需修改對(duì)應(yīng)參數(shù)即可。
    步驟 1:設(shè)置當(dāng)前架構(gòu)的參數(shù)
# 目標(biāo)架構(gòu)(根據(jù)需要替換為 armeabi-v7a、x86_64 等)
TARGET_ARCH=arm64-v8a

# NDK 對(duì)應(yīng)的工具鏈架構(gòu)(arm64-v8a 對(duì)應(yīng) aarch64,armeabi-v7a 對(duì)應(yīng) arm,x86_64 對(duì)應(yīng) x86_64)
TOOLCHAIN_ARCH=aarch64

# Android 最小支持版本(根據(jù)需求修改,如 21 及以上支持 64 位)
MIN_SDK_VERSION=21

# 構(gòu)建目錄(每個(gè)架構(gòu)單獨(dú)一個(gè)目錄,避免沖突)
BUILD_DIR=$(pwd)/build_$TARGET_ARCH
mkdir -p $BUILD_DIR
cd $BUILD_DIR

步驟 2:生成 CMake 交叉編譯配置
使用 NDK 提供的 build/cmake/android.toolchain.cmake 工具鏈文件,指定交叉編譯參數(shù):

cmake \
  -DCMAKE_SYSTEM_NAME=Android \  # 目標(biāo)系統(tǒng)為 Android
  -DCMAKE_SYSTEM_VERSION=$MIN_SDK_VERSION \  # 最小 SDK 版本
  -DCMAKE_ANDROID_ARCH_ABI=$TARGET_ARCH \  # 目標(biāo) ABI
  -DCMAKE_ANDROID_NDK=$NDK_PATH \  # NDK 路徑
  -DCMAKE_ANDROID_STL_TYPE=c++_shared \  # 使用 C++ 共享庫(kù)(需與應(yīng)用一致)
  -DCMAKE_BUILD_TYPE=Release \  #  Release 模式(優(yōu)化編譯)
  -DCMAKE_INSTALL_PREFIX=$OUTPUT_DIR/$TARGET_ARCH \  # 安裝路徑(按架構(gòu)區(qū)分)
  -DBUILD_SHARED_LIBS=ON \  # 編譯動(dòng)態(tài)庫(kù)(.so)
  -DBUILD_STATIC_LIBS=OFF \  # 不需要靜態(tài)庫(kù)
  -Dprotobuf_BUILD_TESTS=OFF \  # 禁用測(cè)試(加速編譯)
  -Dprotobuf_BUILD_EXAMPLES=OFF \  # 禁用示例
  $PROTOBUF_SRC/cmake  # cmakelist.txt 所在目錄

步驟 3:編譯并安裝

# 編譯(-j 后面的數(shù)字為并行線程數(shù),根據(jù) CPU 核心數(shù)調(diào)整,如 8)
make -j8

# 安裝到指定的輸出目錄($OUTPUT_DIR/$TARGET_ARCH)
make install

編譯完成后,$OUTPUT_DIR/$TARGET_ARCH/lib*** 目錄下會(huì)生成 libprotobuf.so 和 libprotobuf-lite.so(輕量版)。

  1. 編譯其他架構(gòu)
    替換 TARGET_ARCH 和 TOOLCHAIN_ARCH 重復(fù)上述步驟即可:
    armeabi-v7a:
TARGET_ARCH=armeabi-v7a
TOOLCHAIN_ARCH=arm
MIN_SDK_VERSION=16  # 32 位最低支持 16

x86_64:

TARGET_ARCH=x86_64
TOOLCHAIN_ARCH=x86_64
MIN_SDK_VERSION=21
2.png

最后,將以上步驟寫成一個(gè)腳本文件,只需要在一個(gè)空文件夾下執(zhí)行腳本即可。

protobuf_android.sh

#!/bin/bash

# 配置參數(shù)(請(qǐng)根據(jù)實(shí)際環(huán)境修改)
NDK_PATH="/Users/xxx/Library/Android/sdk/ndk/26.1.10909125" # Android NDK 路徑
PROTOBUF_VERSION="v3.19.6"         # Protobuf 版本
OUTPUT_ROOT="$PWD/protobuf-android-build"  # 輸出目錄根路徑
MIN_SDK_ARM64=21                   # arm64-v8a 最小支持 SDK 版本
MIN_SDK_ARM32=21                   # armeabi-v7a 最小支持 SDK 版本
MIN_SDK_X86=21                     # x86_64 最小支持 SDK 版本
THREADS=4                          # 并行編譯線程數(shù)(根據(jù) CPU 核心數(shù)調(diào)整)

# 檢查 NDK 是否存在
if [ ! -d "$NDK_PATH" ]; then
    echo "錯(cuò)誤:NDK 路徑不存在,請(qǐng)檢查 NDK_PATH 配置"
    exit 1
fi

# 檢查依賴工具
check_dependency() {
    if ! command -v $1 &> /dev/null; then
        echo "錯(cuò)誤:未找到 $1,請(qǐng)先安裝(可通過(guò) brew install $1)"
        exit 1
    fi
}
check_dependency "git"
check_dependency "cmake"
check_dependency "make"

# 添加vpn終端代理命令,加速?gòu)膅ithub上下載代碼
#export https_proxy=http://xxx:端口 http_proxy=http://xxx:端口 all_proxy=socks5://xxx:端口

# 克隆并準(zhǔn)備 Protobuf 源碼
echo "===== 下載 Protobuf $PROTOBUF_VERSION 源碼 ====="
if [ ! -d "protobuf" ]; then
    git clone https://github.com/protocolbuffers/protobuf.git || exit 1
fi
cd protobuf || exit 1
git checkout $PROTOBUF_VERSION || exit 1
git submodule update --init --recursive || exit 1
PROTOBUF_SRC=$(pwd)
cd .. || exit 1

# 創(chuàng)建輸出目錄
mkdir -p "$OUTPUT_ROOT"

# 交叉編譯函數(shù)(參數(shù):目標(biāo)架構(gòu) 工具鏈架構(gòu) 最小 SDK 版本)
build_arch() {
    local TARGET_ARCH=$1
    local TOOLCHAIN_ARCH=$2
    local MIN_SDK=$3
    local BUILD_DIR="$PWD/build_$TARGET_ARCH"
    local INSTALL_DIR="$OUTPUT_ROOT/$TARGET_ARCH"

    echo -e "\n===== 開(kāi)始編譯 $TARGET_ARCH 架構(gòu) ====="
    echo "構(gòu)建目錄: $BUILD_DIR"
    echo "安裝目錄: $INSTALL_DIR"

    # 創(chuàng)建并進(jìn)入構(gòu)建目錄
    mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR" || exit 1

    # 運(yùn)行 CMake 配置
    cmake \
        -DCMAKE_SYSTEM_NAME=Android \
        -DCMAKE_SYSTEM_VERSION=$MIN_SDK \
        -DCMAKE_ANDROID_ARCH_ABI=$TARGET_ARCH \
        -DCMAKE_ANDROID_NDK=$NDK_PATH \
        -DCMAKE_ANDROID_STL_TYPE=c++_shared \
        -DCMAKE_BUILD_TYPE=Release \
        -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" \
        -DBUILD_SHARED_LIBS=ON \
        -DBUILD_STATIC_LIBS=OFF \
        -Dprotobuf_BUILD_TESTS=OFF \
        -Dprotobuf_BUILD_EXAMPLES=OFF \
        "$PROTOBUF_SRC/cmake" || {
        echo "CMake 配置 $TARGET_ARCH 失敗"
        exit 1
    }

    # 編譯并安裝
    make -j$THREADS || {
        echo "編譯 $TARGET_ARCH 失敗"
        exit 1
    }
    make install || {
        echo "安裝 $TARGET_ARCH 失敗"
        exit 1
    }

    # 返回上層目錄
    cd .. || exit 1
    echo "===== $TARGET_ARCH 編譯完成 ====="
}

# 編譯各架構(gòu)(按需求注釋不需要的架構(gòu))
build_arch "arm64-v8a" "aarch64" $MIN_SDK_ARM64
build_arch "armeabi-v7a" "arm" $MIN_SDK_ARM32
build_arch "x86_64" "x86_64" $MIN_SDK_X86

echo -e "\n===== 所有架構(gòu)編譯完成 ====="
echo "輸出目錄: $OUTPUT_ROOT"
echo "各架構(gòu) .so 文件路徑:"
find "$OUTPUT_ROOT" -name "libprotobuf.so"

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容