最近收到一個(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ù)。
安裝依賴工具
確保已安裝基礎(chǔ)編譯工具cmake和版本控制工具git,使用brew安裝即可。下載 Android NDK
交叉編譯 Android 庫(kù)需要 NDK 提供的工具鏈,可以直接去https://developer.android.com/ndk/downloads/older_releases官網(wǎng)下載,也可以在Android Studio的SDK Manager中下載。下載完成解壓到指定目錄,這個(gè)目錄我們稱為NDK_PATH,也可以將它配置環(huán)境變量中-
下載 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ù)。
-
定義編譯參數(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
-
針對(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(輕量版)。
-
編譯其他架構(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

最后,將以上步驟寫成一個(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"