Android NDK 指南
文章目录
前言
编写此文档的用意:
- 作为搭建基础 NDK 工程的教程;
- 作为入门 NDK 工程的参考手册。
NDK 工程构建
可采用三种方式进行 NDK 工程的构建:
- 基于 Make 的 ndk-build,这是传统的 ndk-build 构建方式,使用 Makefile 方式进行构建,简洁高效;
- CMake 是新型的构建方式,CMake 具有跨平台的特性,通过 CMake 生成 Makefile 后再进行构建,CMake 的配置文件可读性更高;
- 其他编译系统,通过引入其他编译系统可对编译过程进行定制,例如引入 obfuscator-llvm 对源码进行混淆和压缩,增强源代码安全性。
下面是每种构建方式的基础示例,使用 Android Studio 4.0 和 NDK 21 进行如下构建。
Android.mk
基于 Android.mk 的产物为 libfoo.so 的 NDK 基本工程搭建。
在 Android 工程的 src/main 下建立 jni 目录(Android.mk 工程的默认文件目录为 jni,也可指定其他目录进行构建,使用命令 ndk-build -C 目录
),工程结构如下:
包含两个 .mk 文件用来描述 NDK 工程,和两个基本的 C++ 语言源文件,结构如下:
src/main
|
+-- java
+-- jni
|
+-- Android.mk
+-- Application.mk
+-- libfoo.h
+-- libfoo.cpp
在 Android Studio 的当前 Module 配置中指明 Android.mk 文件路径:
// app-build.gradle
android {
...
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
}
编写 Android.mk 文件用于向 NDK 构建系统描述工程的 C/C++ 源文件以及共享库的属性。
# Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
# 指定共享库名字,产出物为 libfoo.so
LOCAL_MODULE := foo
# 指定源代码文件,多个源代码文件使用空格分隔,换行在行尾使用 \
LOCAL_SRC_FILES := libfoo.cpp
include $(BUILD_SHARED_LIBRARY)
添加 Application.mk 用于描述 NDK 工程概要设置。
# Application.mk
# 指定生成特定 ABI 的代码
APP_ABI := armeabi-v7a arm64-v8a
APP_OPTIM := debug
在 java 目录创建 Java 类,用于声明 JNI 方法,提供给其他类调用。
// class io.l0neman.mkexample.NativeHandler
public class NativeHandler {
static {
// 加载 libfoo.so 库
System.loadLibrary("foo");
}
public static native String getHello();
}
源代码:
// libfoo.h
#ifndef NDKTPROJECT_LIBFOO_H
#define NDKTPROJECT_LIBFOO_H
#include <jni.h>
extern "C" {
// 注册指定 Java 层的 JNI 方法
JNIEXPORT jstring JNICALL
Java_io_l0neman_mkexample_NativeHandler_getHello(JNIEnv *env, jclass clazz);
}
#endif //NDKTPROJECT_LIBFOO_H
// libfoo.cpp
#include "libfoo.h"
jstring Java_io_l0neman_mkexample_NativeHandler_getHello(JNIEnv *env, jclass clazz) {
return env->NewStringUTF("Hello-jni");
}
这样的话就完成了一个基本的 NDK 工程搭建,编译后调用代码即可得到 java 字符串 "Hello-jni"
。
// MainActivity.java
String hello = NativeHandler.getHello();
- 提示
Android.mk 和 Application.mk 中可使用的系统变量请参考下文。
Android.mk 只是 Makefile 的片段,对于 Makefile 本身的熟悉有助于深入理解和编写 Android.mk,可参考 Makfile 指南
CMake
使用 CMake 和 Android.mk 在 Android Studio 中的构建步骤类似,如下:
基于 CMake 的产出物为 libfoo.so 的 NDK 基本工程搭建。
在 Android 工程的 src/main 下建立 cpp 目录,工程结构如下:
包含一个 CMakeLists.txt 文件来描述 NDK 工程,和两个基本的 C++ 语言文件。
src/main
|
+-- java
jni
|
+-- CMakeLists.txt
+-- libfoo.h
+-- libfoo.cpp
在 Android Studio 的当前 Module 配置中指明 CMakeLists.txt 文件路径:
// app/build.gradle
android {
...
externalNativeBuild {
cmake {
path 'src/main/cpp/CMakeLists.txt'
}
}
}
编写 CMakeLists.txt 文件用于向 NDK 构建系统描述工程的 C/C++ 源文件以及共享库的属性。
# CMakeLists.txt
cmake_minimum_required(VERSION 3.4.3)
add_library(
# 共享库名字,生产物为 libfoo.so
foo
# 编译为共享库 .so
SHARED
# 源代码文件,多个文件使用空格分隔或换行
main.cpp
)
此时将 Android.mk 工程中的 Java 源文件 NativeHandler.java 复制过来,将 libfoo.cpp 和 libfoo.h 内容填入中即可直接编译测试。
独立工具链
有时编译 NDK 工程有一些特殊需求,例如对代码进行混淆,加入第三方编译器 obfuscator-llvm 对 NDK 工程进行编译。这时就需要搭建第三方工具链的编译环境,将它加入 NDK 的一般构建过程中。
下面是一个引入 obfuscator-llvm 编译器编译代码的示例。
obfuscator-llvm 构建
环境:android-ndk-r14b,目前已知此版本可支持 obfuscator-llvm 的编译配置
ndk r14b 下载地址:https://developer.android.google.cn/ndk/downloads/older_releases
- 首先下载编译器,指定最新版本的 obfuscator-llvm 分支,将仓库克隆至本地
git clone -b llvm-4.0 https://github.com/obfuscator-llvm/obfuscator.git
- 编译出编译器的可执行文件
过程如下,以下命令 Windows DOS 和 Linux Shell 中可通用:
- 进入编译器仓库目录中
cd obfuscator
; - 创建临时文件目录
mkdir build
; - 进入临时文件目录
cd build
; - 使用 CMake 生成 Makefile 或者 Vs 解决方案:
如果没有按照 CMake,可去 CMake 官网下载安装。
cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_INCLUDE_TESTS=OFF ../
CMake 将会自动检测电脑上的编译器环境,如果是 Linux,生成 Makefile,如果 Windows 上安装了 Visual Studio,将生成解决方案文件。
- 编译编译器源代码:
Linux 上执行:
make -j4
Windows 平台建议使用 Visual Studio 进行编译,直接打开 build 中的 LLVM.sln,然后生成解决方案(Build Solution)。
编译过程需要持续 30 分钟或更长时间,取决于电脑配置 CPU 性能。
编译过程中有可能出现错误,需要自己解决出现的不同情况,编译完成后将生成所需的 bin 和 lib 目录(Release 中)。
- 配置 NDK 环境
设原始 NDK 工具链根目录为 android-ndk-r14b。
进入 android-ndk-r14b/toolchains 目录中,复制已存在的 llvm 目录到 ollvm-4.0,Linux 使用 cp llvm ollvm-4.0
,Windows 复制文件出现 llvm-副本
后重命名为 ollvm-4.0
。
Windows 平台将上面编译出来的 bin 和 lib 放入 ollvm-4.0/prebuilt/windows-x86_64 中,Linux 平台放入 ollvm-4.0/prebuilt/linux-x86_64 中