刚好最近有需要编写安卓端的可执行二进制文件,以便在安卓端直接通过 shell 调用执行。官方推荐使用 Android Studio 进行开发,但是个人感觉再装一个 IDE 很麻烦,其次我不清楚 Android Studio 除了用来做 jnilibs ,能不能编译和调试二进制文件。(碰巧看到这里又知道情况的朋友可以留言告诉我一下,感谢!)所以我研究了一下怎么直接用 Clion 来编写并通过 adb 远程调试。作为小项目,最后效果也算满意。踩了一些坑,特此记录一下。

环境准备

本文在 Linux 下进行,但是 Widows 下同理,可作为参考。

  • 首先需要下载 Android NDK ,这个不必多说: Android NDK 下载
  • 需要 cmake、make、gdb 工具, Windows 下应该可通过 MinGW 解决
  • Android Platform Tools ,提供了 adb ,直接下载就行: Android Platform Tools 下载

至此,环境准备完毕!

Clion 设置

Clion 的设置,首先在于新建项目的时候,选择新建 cmake 项目。然后会自动生产 main.c 和 CMakeLists.txt 和 cmake-build-debug 目录。很简单的两个文件, main.c 输出一个 hello word 。然后修改 CmakeList.txt ,怎么来修改呢?参考:CMake 文档: 使用 NDK 为安卓平台交叉编译 ,我的项目样例如下:

cmake_minimum_required(VERSION 3.22) # cmake 版本
project(android_server C) # 我的项目名叫 android_server ,自行对应调整

set(CMAKE_C_STANDARD 23) # C 版本,自行调整


set(CMAKE_SYSTEM_NAME Android) # 固定
set(CMAKE_SYSTEM_VERSION 24) # Android API level ,自行决定
set(CMAKE_ANDROID_ARCH_ABI arm64-v7a) # 目标二进制平台,自行决定
set(CMAKE_ANDROID_NDK /这里放下载解压的 Android NDK 的文件夹路径)
set(CMAKE_ANDROID_STL_TYPE gnustl_static) # 反正我用的静态链接

set(CMAKE_C_COMPILER /这里放下载解压的 Android NDK 的文件夹路径/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang) # 自行灵活调整,注意 armv7a 还是别的,也是自行调整
set(CMAKE_CPP_COMPILER /这里放下载解压的 Android NDK 的文件夹路径/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++) # 同上

set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g") # 调试模式,没啥好说的
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g") # 同上

add_executable(android_server main.c)

修改完 CMakeLists.txt 后,把 Clion 默认的 build、debug 方案全部删除,按钮如下图(菜单左上角的加减号即为增删方案按钮):

点击此处编辑 Clion 构建方案
图1:点击编辑 Clion 构建方案

进去把原来的全部删除后,新增方案:

新增方案样例
图2:点击加号新增构建方案

其中只有一个点比较特别,端口 12345 怎么来的呢?要知道,这是 GDB 远程调试,也就是说,以我的项目为例,二进制跑在安卓 Arm64-v7a平台上,这里只能远程调试。而通过 adb shell gdbserver 绑定的安卓端口只在其本地,所以需要 adb forward 指令进行转发,这个 12345 就是我转发的 PC 上的端口,具体可看后面的脚本命令。

那么总结一下,流程应该是,本地配置好交叉编译参数,然后通过 NDK 提供的编译工具,编译生成可在安卓端运行的二进制文件。然后通过 gdbserver 进行远程调试。既然要使用 NDK 提供的编译工具,当然还要设置一下工具链。虽然根据文档在 cmake 文件里面设置了 C/C++ Compiler 路径,但是若点击 Clion 的 Build 按钮,实际测试未生效。所以如果想 Clion 的 Build 按钮生效,还需要参考下图所示新增安卓编译工具链的配置(编译器路径自然是和 cmake 里面的配置一样),并将其设置为默认。因为没找到项目级的设置方案,所以后面写 PC 代码的时候还要记得切回来,不然编译出来运行很自然会报错 —— 可执行文件格式错误。当然了,如果只使用后面提供的脚本命令,不使用 Clion 的 Build 按钮,这一步是可以省略的。

新增安卓编译工具链设置
图3:新增安卓编译工具链设置

那么到现在,剩两个问题:

  1. 安卓端文件运行权限问题
  2. 端口怎么转发、 Clion 怎么连接

首先得跑起来,不然问题 2 不用看。经过测试,安卓端丢进一般路径如 /sdcard 是不能运行的,但是 /data/local/tmp/ 目录下可以,还能调整执行权限(要是开了 Root 就不说了)。而问题 2 ,应该问题不大,查阅文档熟悉相关命令的使用就行。

运行与调试

对于上面的两个问题,我编写了两个简单的脚本,都在里面了,首先是编译后在安卓直接运行(可执行文件名为 android_server ):

#!/bin/bash

cd ./cmake-build-debug
cmake ..
make
adb push ./android_server /data/local/tmp/
adb shell chmod a+x /data/local/tmp/android_server
adb shell /data/local/tmp/android_server

第二个是编译后在安卓启动 gdbserver 等待调试器连接:

#!/bin/bash

adb forward tcp:12345 tcp:1234
cd ./cmake-build-debug
cmake ..
make
adb push ./android_server /data/local/tmp/
adb shell chmod a+x /data/local/tmp/android_server
adb shell gdbserver 0.0.0.0:1234 /data/local/tmp/android_server

留意上面第一条指令,进行了端口转发。然后,该指令可以多次重复运行不冲突,所以可以放在这里。于是乎,我们的测试运行与开启调试端口都 ok 了。测试一下:

成功调试截图
图4:成功调试截图

其他问题

到这里其实还有两个问题:

  1. Clion 的代码提示环境得是安卓 NDK 的,不能是本机的
  2. 每次调试先运行脚本,再点一下 bug 图标很麻烦

对于问题 1 ,可以在 External Libraries 上右键,再点击 Reload Cmake Project ,然后提示和跟进都是 ok 的了。我这里截图抓不到 Clion 的悬浮菜单,可以看下图中对 struct stat 跟进的结果,已经是 NDK 里面的 sys/stat.h 了:

右键点击刷新 Cmake 项目,代码提示跟进成功
图5:右键点击刷新 Cmake 项目,代码提示跟进成功

对于问题 2 ,理想的结果就是,我每次更新完代码, debug 之前自动调用一下,可惜的是我摸索了好一会儿,最后发现 Clion 貌似确实不支持设置 debug 前置动作。那么怎么解决呢?如果写过 Web 前端,使用诸如 Vue 之类的工具,每次更新完代码后相应的 Web 预览就自动刷新了。如果参考这种做法,就是监听文件动作,然后一旦有更新就执行对应脚本。如果自己写工具, Linux 平台参考 inotify API 即可,或者找一下称手的开源代码。我目前项目太小用不着,就不继续折腾了,到此为止。