android-ndk开发(5): 编译运行 hello-world

android-ndk开发(5): 编译运行 hello-world

2025/05/05

1. 概要

hello-world 是每一门语言的第一个样例程序, 跑通它, 在一段时间内你会相当顺畅: 可以边学边实验, 根据运行结果得到反馈。

而对于 android-ndk 开发而言, 网络上找不到好用的 android-ndk 控制台样例程序, 仅就如何交叉编译这一件事情, 就鲜有说的足够简洁、 足够现代、 足够清晰的。

即便编译出了可执行程序, 关于如何运行, 容易卡在不知道 /data/local/tmp 这一特殊目录的盲点上, 导致再次劝退一拨人。

2. 现状

2.1 找不到好用的 android-ndk 控制台样例程序

11年前,有人在 stackoverflow 发帖提问, "我能用 android ndk 构建控制台应用程序吗?":

https://stackoverflow.com/questions/27496300/can-i-use-the-ndk-to-build-command-line-apps-on-android

在11年后的2025年, 这个问题只有一个答案, 提到的参考文档也无从找起。 这个问答本身是失败的。

5年前, songdongsheng 在自己的博客中, 用 ndk-build 这一落后的构建工具, 给出了 android 控制台应用的样例工程:

https://songdongsheng.github.io/2020/04/05/ndk-console-program/

相比于 StackOverFlow 的问答, 这显然进步了。 但是还是过于复杂和落后。

2年前, codeljo 也做了类似的事情, 不过不是写成博客, 而是给出基于 ndk-build 的工程:

https://github.com/codeljo/ndk_command_line_app

android ndk 官方就一点点例子都不提供么? 也不是。 普通人肯定能想到用 "android ndk examples" 作为关键字去搜索, 找到 "ndk-samples" 这一官方仓库; 并在一段时间后, 找到 hello-jni 这个例子:

https://github.com/android/ndk-samples/blob/main/hello-jni/app/src/main/cpp/CMakeLists.txt

然而 hello-jni 的重点在于怎么使用 JNI 在 cpp 和 java 之间交互, 顺带用 cmake 而不是落后的 ndk-build 作为构建描述。 注意,它构建的是动态库, 而不是可执行程序。

2.2 /data/local/tmp 目录

初学者如果是一个人瞎鼓捣, 至少在没有 AI 的年代, 恐怕半小时之内是无法知道 /data/local/tmp 这一特殊目录的存在的。

这就像是一个知识诅咒, 当知道了这个目录, 事情很简单; 当不知道这个目录, 会被误导 "你需要 root 过的 android 手机". 而我按照老大的指示, 取找另一位大佬Q2借设备时, Q2满脸杀气的说: "我从没听说过, 没有 root 过的 android 机就不能用"。

而使用 "/data/local/tmp site developer.android.com" 作为关键字在谷歌搜索, 结果的详情页面中, android 官方始终没有提及 /data/local/tmp 目录。

嗯, 官方藏着掖着使绊子, 就不让普通小白以最快速度跑通 hello-world。

3. 动手搞 hello-world

本节给出, 动手写 hello-world 代码, 以及交叉编译到 android-arm64, 并在设备上运行的步骤。 给出极简版本, 以及中规中矩的 cmake 版本。

3.1 极简版本

编写 hello.c:

c 复制代码
#include <stdio.h>
int main() {
    printf("hello, world!\n");
}

编写 build-and-run.sh, 参考了 https://developer.android.google.cn/ndk/guides/other_build_systems?hl=zh-cn

bash 复制代码
#!/bin/bash
NDK=~/soft/android-ndk/r21e
HOST_TAG=darwin-x86_64
$NDK/toolchains/llvm/prebuilt/$HOST_TAG/bin/clang \
    -target aarch64-linux-android21 hello.c \
    -o hello

adb push hello /data/local/tmp/hello
adb shell "cd /data/local/tmp; chmod +x ./hello; ./hello"

连接设备: 掏出手机,找到无线调试

bash 复制代码
$ adb pair 192.168.3.102:44033
Enter pairing code: 428573
* daemon not running; starting now at tcp:5037
* daemon started successfully

运行:

bash 复制代码
$ bash ./build-and-run.sh
hello: 1 file pushed, 0 skipped. 63.6 MB/s (8072 bytes in 0.000s)
hello, world!

3.2 中规中矩的 cmake 版本

编写 CMakeLists.txt:

cmake 复制代码
cmake_minimum_required(VERSION 3.20)
project(hello)
add_executable(hello hello.c)

编写 build-and-run-cmake.sh:

bash 复制代码
#!/bin/bash
NDK=~/soft/android-ndk/r21e
cmake \
    -S . \
    -B build \
    -G Ninja \
    -DCMAKE_TOOLCHAIN_FILE=$NDK/build/cmake/android.toolchain.cmake \
    -DANDROID_ABI=arm64-v8a \
    -DANDROID_PLATFORM=21

cmake --build build

adb push build/hello /data/local/tmp/hello
adb shell "cd /data/local/tmp; chmod +x ./hello; ./hello"

运行:

bash 复制代码
➜  android_ndk git:(main) ✗ bash build-and-run-cmake.sh
...
-- Configuring done (0.0s)
-- Generating done (0.0s)
-- Build files have been written to: /Users/zz/work/zcnn/doc/devops/android_ndk/build
[2/2] Linking C executable hello
build/hello: 1 file pushed, 0 skipped. 83.0 MB/s (9616 bytes in 0.000s)
hello, world!

3.3 解释

极简版本, 主打一个简单, 不依赖 cmake 和 ninja。

即便是 cmake 版本, 也保持了使用 hello.c 而不是 hello.cpp 文件。 从 C 换 C++ 是比较 trivial 的, 咱们现在忽略。

指定了使用 arm64-v8a 的 ABI, 或者说, 目标架构是 arm64。 也叫做 aarch64。 为啥不弄别的架构的? 因为 arm64 是用的最多的、 每个人都感悟得到的。 android 手机, 或者您的尊贵的苹果手机, 都是 arm64 架构的。

android 也可以编译到 x86_64, x86, armv7, armv7 with neon 的 ABI。

4. 总结

批判了 anroid ndk 官方故意使绊子的问题。 给出了绝佳的 android-ndk hello-world 控制台可执行程序的例子。

相关推荐
baiyu3311 小时前
android-ndk开发(1): 搭建环境
android-ndk