https://gist.github.com/gnuanu/252fd406f48f7da2c1c7

Android NDK编译C/C++为so共享对象_wkw1125的博客-CSDN博客_ndk编译so

https://developer.android.com/ndk/guides/build?hl=zh-cn

Building an Android command-line application using the NDK build tools

opencl

Android with OpenCL tutorial | aplacetogeek

Android设备上运行OpenCL - 知乎

使用 NDK 编译代码主要有三种方法:

ndk, sdk下载

sdk manager命令行工具下载:

https://developer.android.com/studio?hl=zh-cn#command-tools
https://developer.android.com/studio/command-line/sdkmanager

安装java 

sudo apt install -y default-jre default-jdk

 列出可以安装的包

./bin/sdkmanager --list --channel=0 --sdk_root=./sdk
例如:

build-tools;33.0.0
cmake;3.18.1
cmdline-tools;latest
emulator
ndk;20.1.5948944
platform-tools
platforms;android-30
sources;android-32

安装sdk ndk等一系列包

./bin/sdkmanager \
  "platform-tools" \
  "platforms;android-30" \
  "sources;android-30" \
  "cmake;3.18.1" \
  "build-tools;33.0.0" \
  "cmdline-tools;latest" \
  "ndk;24.0.8215888" \
  --channel=0 --sdk_root=./sdk

sdk:java开发; ndk:C++开发; 应用通常使用java通过jni方式调用C++。java函数生成C++头文件,然后使用C++进行实现。

基于ndk-build开发命令行程序

jni目录里面3个文件:

Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := hello_world
LOCAL_SRC_FILES := hello_world.cpp
# LOCAL_C_INCLUDES += ../jni/

# include $(BUILD_SHARED_LIBRARY)
include $(BUILD_EXECUTABLE)

Application.mk

# APP_ABI := all
# APP_ABI := armeabi-v7a arm64-v8a x86

APP_ABI := arm64-v8a

APP_PLATFORM := android-21

# APP_STL := c++_shared
APP_STL := c++_static

hello_world.cpp

#include <iostream>

int main() {
  std::cout << "hello world c++" << std::endl;
  return 0;
}

然后在当前目录执行ndk-build,会在libs/arm64-v8a/生成可执行文件。

最后拷贝文件到手机端执行:

export DEV_DIR=/data/local/tmp/

adb push ./libs/arm64-v8a/hello_world ${DEV_DIR}

adb shell #进入shell命令行执行环境
cd ${DEV_DIR}
chmod 755 ./hello_world
./hello_world

基于CMake命令行开发

仍然是三个文件

build.sh

#!/bin/bash
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

cd ${SCRIPT_DIR}

# rm -rf build
mkdir build
cd build

# please set SDK_PATH, NDK_PATH first
# SDK_PATH=/root/codes/my_sdk/sdk/
# NDK_PATH=/root/codes/my_sdk/sdk/ndk/24.0.8215888/
ANDROID_ABI=arm64-v8a
MINSDKVERSION=29

# please ref https://developer.android.com/ndk/guides/cmake?hl=zh-cn#command-line
# /root/codes/my_sdk/sdk/cmake/3.18.1/bin/cmake \
#     -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
#     -DANDROID_ABI=${ANDROID_ABI} \
#     -DANDROID_PLATFORM=android-${MINSDKVERSION} \
#     -DANDROID_STL=c++_static \
#     ..

# make

${SDK_PATH}/cmake/3.18.1/bin/cmake \
    -DCMAKE_BUILD_TYPE=Release \
    -DCMAKE_TOOLCHAIN_FILE=${NDK_PATH}/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=${ANDROID_ABI} \
    -DANDROID_NDK=${NDK_PATH} \
    -DANDROID_PLATFORM=android-${MINSDKVERSION} \
    -DCMAKE_ANDROID_ARCH_ABI=${ANDROID_ABI} \
    -DCMAKE_ANDROID_NDK=${NDK_PATH} \
    -DCMAKE_MAKE_PROGRAM=${SDK_PATH}/cmake/3.18.1/bin/ninja \
    -DCMAKE_SYSTEM_NAME=Android \
    -DCMAKE_SYSTEM_VERSION=${MINSDKVERSION} \
    -DANDROID_STL=c++_static \
    -GNinja \
    ..
if [ $? -ne 0 ]; then
    echo "ERROR: cmake failed"
    exit 1
fi

 
${SDK_PATH}/cmake/3.18.1/bin/ninja
if [ $? -ne 0 ]; then
    echo "ERROR: build failed"
    exit 1
fi

# DANDROID_ABI:
#     armeabi-v7a 
#     armeabi-v7a with NEON
#     arm64-v8a   
#     x86 
#     x86_64

# ANDROID_STL:
#     c++_shared  libc++ 的共享库变体。
#     c++_static  libc++ 的静态库变体。
#     none    不支持 C++ 标准库。
#     system  系统 STL

其他cmake方式

cmake .. \
    -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI="arm64-v8a" -DANDROID_NATIVE_API_LEVEL=android-21 \
    -DANDROID_STL=c++_shared \
    -DWITH_TESTING=OFF \
    -DWITH_PYTHON=OFF \
    -DANDROID_TOOLCHAIN=clang \
    -DWITH_ICU_LITE=ON 
make \
 -j8

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)

project(cmake_study LANGUAGES CXX)

# set(CMAKE_CXX_STANDARD 11)

# without these flags, the cmake generated binary file is much bigger than ndk-build
# you can also pass -DCMAKE_C_FLAGS="-s" to the CMake call.
set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s")

# 编译源码生成目标
add_executable(
    main
    jni/hello_world.cpp
)

# target_include_directories(
#     main
#     PUBLIC
#     inc
# )
# target_link_libraries(
# 	main
# 	PUBLIC 
# 	libxx.so
# )

这里面如果不加FLAGS则会导致CMake生成的二进制明显比ndk-build生成的更大,ref:

c++ - How to config cmake for strip file - Stack Overflow

Android CMake and ndk-build produce different sized files - Stack Overflow

hello_world.cpp

#include <iostream>

int main() {
  std::cout << "hello world c++ with cmake" << std::endl;
  return 0;
}

执行build.sh即可构建并生成二进制文件

PaddleNLP/how_to_build_android.md at develop · PaddlePaddle/PaddleNLP · GitHub

编译后的 C++ 库在当前目录下的 cpp 目录下。可以选择使用 strip 减少库体积:

$NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/aarch64-linux-android/bin/strip libcore_tokenizers.so

错误处理

ndk/20.1.5948944/toolchains/llvm/prebuilt/linux-x86_64/bin/../lib/gcc/aarch64-linux-android/4.9.x/../../../../aarch64-linux-android/bin/ld: jni/../lib64/libOpenCL.so: don't know how to handle section `.relr.dyn' [0x      13]

解决方法:换一个高版本的ndk


sh脚本运行:

sh开头: #!/system/bin/sh

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐