Android NDK 编译 protobuf

最近收到一个小伙伴的私信,Android平台帮忙编译一个Native C++项目,这个项目中用到了Protobuf 和OpenCV两个C++库,其中OpenCV有现成的动态库.so了,但是Protobuf并没有现成的,需要自己拿源码编译!废话不多说,直接上干货...

首先,准备编译所需要的工具

在 macOS M4(ARM 架构)上为 Android 交叉编译 Protobuf (如v3.19.6版本)的 .so 动态库,需要使用 Android NDK 配置交叉编译环境,针对不同的 Android CPU 架构(如 arm64-v8a、armeabi-v7a、x86_64 等)生成对应版本的库。

  1. 安装依赖工具

    确保已安装基础编译工具cmake 和版本控制工具git,使用brew安装即可。

  2. 下载 Android NDK

    交叉编译 Android 库需要 NDK 提供的工具链,可以直接去官网下载, 也可以在Android Studio的SDK Manager中下载。下载完成解压到指定目录,这个目录我们称为NDK_PATH,也可以将它配置环境变量中

  3. 下载 Protobuf 3.19.6 源码

bash 复制代码
    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 交叉编译,需为每个目标架构(如 arm64-v8a、armeabi-v7a)单独配置编译参数。

1. 定义编译参数(通用配置

先设置通用变量,方便后续复用:

bash 复制代码
    # NDK 路径(替换为你的实际路径)
    export NDK_PATH=/Users/xxx/Library/Android/sdk/ndk/26.1.10909125

    # Protobuf 源码根目录(当前目录)
    export PROTOBUF_SRC=$(pwd)

    # 输出目录(所有架构的 .so 最终会放到这里)
    export OUTPUT_DIR=$(pwd)/android_build
    mkdir -p $OUTPUT_DIR

2. 针对不同架构编译(以 arm64-v8a 为例)

Android 主流架构包括: arm64-v8a(64 位 ARM,主流新设备) armeabi-v7a(32 位 ARM,老旧设备) x86_64(64 位 x86,模拟器常用) 以下以 arm64-v8a 为例,其他架构仅需修改对应参数即可。

步骤 1:设置当前架构的参数

ini 复制代码
    # 目标架构(根据需要替换为 armeabi-v7a、x86_64 等)
    TARGET_ARCH=arm64-v8a

    # NDK 对应的工具链架构(arm64-v8a 对应 aarch64,armeabi-v7a 对应 arm,x86_64 对应 x86_64)
    TOOLCHAIN_ARCH=aarch64

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

    # 构建目录(每个架构单独一个目录,避免冲突)
    BUILD_DIR=$(pwd)/build_$TARGET_ARCH
    mkdir -p $BUILD_DIR
    cd $BUILD_DIR

步骤 2:生成 CMake 交叉编译配置

使用 NDK 提供的 build/cmake/android.toolchain.cmake 工具链文件,指定交叉编译参数:

ini 复制代码
    cmake \
      -DCMAKE_SYSTEM_NAME=Android \  # 目标系统为 Android
      -DCMAKE_SYSTEM_VERSION=$MIN_SDK_VERSION \  # 最小 SDK 版本
      -DCMAKE_ANDROID_ARCH_ABI=$TARGET_ARCH \  # 目标 ABI
      -DCMAKE_ANDROID_NDK=$NDK_PATH \  # NDK 路径
      -DCMAKE_ANDROID_STL_TYPE=c++_shared \  # 使用 C++ 共享库(需与应用一致)
      -DCMAKE_BUILD_TYPE=Release \  #  Release 模式(优化编译)
      -DCMAKE_INSTALL_PREFIX=$OUTPUT_DIR/$TARGET_ARCH \  # 安装路径(按架构区分)
      -DBUILD_SHARED_LIBS=ON \  # 编译动态库(.so)
      -DBUILD_STATIC_LIBS=OFF \  # 不需要静态库
      -Dprotobuf_BUILD_TESTS=OFF \  # 禁用测试(加速编译)
      -Dprotobuf_BUILD_EXAMPLES=OFF \  # 禁用示例
      $PROTOBUF_SRC/cmake  # cmakelist.txt 所在目录
      

步骤 3:编译并安装

bash 复制代码
    # 编译(-j 后面的数字为并行线程数,根据 CPU 核心数调整,如 8)
    make -j8

    # 安装到指定的输出目录($OUTPUT_DIR/$TARGET_ARCH)
    make install

编译完成后,OUTPUT_DIR/TARGET_ARCH/lib*** 目录下会生成 libprotobuf.solibprotobuf-lite.so(轻量版)。

3. 编译其他架构

替换 TARGET_ARCH 和 TOOLCHAIN_ARCH 重复上述步骤即可:

armeabi-v7a:

ini 复制代码
    TARGET_ARCH=armeabi-v7a
    TOOLCHAIN_ARCH=arm
    MIN_SDK_VERSION=16  # 32 位最低支持 16

x86_64:

ini 复制代码
    TARGET_ARCH=x86_64
    TOOLCHAIN_ARCH=x86_64
    MIN_SDK_VERSION=21

最后,将以上步骤写成一个脚本文件,只需要在一个空文件夹下执行脚本即可。

protobuf_android.sh

bash 复制代码
#!/bin/bash

# 配置参数(请根据实际环境修改)
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                          # 并行编译线程数(根据 CPU 核心数调整)

# 检查 NDK 是否存在
if [ ! -d "$NDK_PATH" ]; then
    echo "错误:NDK 路径不存在,请检查 NDK_PATH 配置"
    exit 1
fi

# 检查依赖工具
check_dependency() {
    if ! command -v $1 &> /dev/null; then
        echo "错误:未找到 $1,请先安装(可通过 brew install $1)"
        exit 1
    fi
}
check_dependency "git"
check_dependency "cmake"
check_dependency "make"

# 添加vpn终端代理命令,加速从github上下载代码
#export https_proxy=http://xxx:端口 http_proxy=http://xxx:端口 all_proxy=socks5://xxx:端口

# 克隆并准备 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

# 创建输出目录
mkdir -p "$OUTPUT_ROOT"

# 交叉编译函数(参数:目标架构 工具链架构 最小 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===== 开始编译 $TARGET_ARCH 架构 ====="
    echo "构建目录: $BUILD_DIR"
    echo "安装目录: $INSTALL_DIR"

    # 创建并进入构建目录
    mkdir -p "$BUILD_DIR" && cd "$BUILD_DIR" || exit 1

    # 运行 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 编译完成 ====="
}

# 编译各架构(按需求注释不需要的架构)
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===== 所有架构编译完成 ====="
echo "输出目录: $OUTPUT_ROOT"
echo "各架构 .so 文件路径:"
find "$OUTPUT_ROOT" -name "libprotobuf.so"
相关推荐
Kapaseker14 小时前
Compose 进阶—巧用 GraphicsLayer
android·kotlin
黄林晴15 小时前
Android17 为什么重写 MessageQueue
android
阿巴斯甜1 天前
Android 报错:Zip file '/Users/lyy/develop/repoAndroidLapp/l-app-android-ble/app/bu
android
Kapaseker2 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
xq95272 天前
Andorid Google 登录接入文档
android
黄林晴2 天前
告别 Modifier 地狱,Compose 样式系统要变天了
android·android jetpack
冬奇Lab2 天前
Android触摸事件分发、手势识别与输入优化实战
android·源码阅读
城东米粉儿2 天前
Android MediaPlayer 笔记
android
Jony_2 天前
Android 启动优化方案
android
阿巴斯甜2 天前
Android studio 报错:Cause: error=86, Bad CPU type in executable
android