个人博客

http://www.milovetingting.cn

前言

记录NDK交叉编译及so库导入Android项目的简单步骤,以备后续用到时查看。

环境

在Linux和Mac环境下,分别编译输出so库。

Red Hat Enterprise Linux 8 64 位 使用GCC编译(也可以用CLANG,这里演示用GCC)

macOS Big Sur 11.3.1 使用CLANG编译(也可以用GCC,这里演示用CLANG)

下载NDK

这里只演示下载NDK17,项目中Mac用到的NDK版本为NDK21

下载NDK

1
wget https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip

NDK18及之后的NDK版本,建议使用CLANG编译。

解压NDK

1
unzip android-ndk-r17c-linux-x86_64.zip

解压后得到android-ndk-r17c文件夹

编写头文件及c文件

GCC编译

1
2
3
4
5
#include "get.h"

int get(){
return 666;
}
1
2
3
4
5
#include "get.h"

int get(){
return 666;
}

CLANG编译

1
2
3
4
#include <stdio.h>

int hi();

1
2
3
4
5
6
#include "hi.h"

int hi(){
return 888;
}

配置NDK

Linux(使用GCC编译)

编辑Home/用户 目录下的.bashrc

1
vim /home/wangyz/.bashrc

添加以下内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 配置NDK的目录
export NDK_HOME=/home/wangyz/NDK/android-ndk-r17c
# 将NDK目录加入PATH中
export PATH=$PATH:$NDK_HOME

# x86 CPU架构的gcc
export NDK_GCC_x86=$NDK_HOME/toolchains/x86-4.9/prebuilt/linux-x86_64/bin/i686-linux-android-gcc

# x86_64 CPU架构的gcc
export NDK_GCC_x64=$NDK_HOME/toolchains/x86_64-4.9/prebuilt/linux-x86_64/bin/x86_64-linux-android-gcc

# ARM CPU架构的gcc
export NDK_GCC_ARM=$NDK_HOME/toolchains/arm-linux-androideabi-4.9/prebuilt/linux-x86_64/bin/arm-linux-androideabi-gcc

# ARM64 CPU架构的gcc
export NDK_GCC_ARM_64=$NDK_HOME/toolchains/aarch64-linux-android-4.9/prebuilt/linux-x86_64/bin/aarch64-linux-android-gcc

# x86 CPU架构 配置sysroot,isystem,否则会找不到头文件
export NDK_GCC_CONFIG_x86="--sysroot=$NDK_HOME/platforms/android-21/arch-x86 -isystem $NDK_HOME/sysroot/usr/include -isystem $NDK_HOME/sysroot/usr/include/i686-linux-android"

# x86_64 CPU架构 配置sysroot,isystem,否则会找不到头文件
export NDK_GCC_CONFIG_x64="--sysroot=$NDK_HOME/platforms/android-21/arch-x86_64 -isystem $NDK_HOME/sysroot/usr/include -isystem $NDK_HOME/sysroot/usr/include/x86_64-linux-android"

# ARM CPU架构 配置sysroot,isystem,否则会找不到头文件
export NDK_GCC_CONFIG_ARM="--sysroot=$NDK_HOME/platforms/android-21/arch-arm -isystem $NDK_HOME/sysroot/usr/include -isystem $NDK_HOME/sysroot/usr/include/arm-linux-androideabi"

# ARM64 CPU架构 配置sysroot,isystem,否则会找不到头文件
export NDK_GCC_CONFIG_ARM_64="--sysroot=$NDK_HOME/platforms/android-21/arch-arm64 -isystem $NDK_HOME/sysroot/usr/include -isystem $NDK_HOME/sysroot/usr/include/aarch64-linux-android"

Mac(使用CLANG编译)

修改~/.bash_profile

1
vim ~/.bash_profile

添加以下内容

1
2
3
4
5
6
7
8
# NDK目录
export NDK_HOME=/Users/ringle/Library/Android/sdk/ndk/21.1.6352462

# CLANG目录
export CLANG=${NDK_HOME}/toolchains/llvm/prebuilt/darwin-x86_64/bin

# 添加到PATH中
export PATH=${PATH}:${NDK_HOME}:${CLANG}

编译

这里编译ARM64构架的so

GCC

1
$NDK_GCC_ARM_64 $NDK_GCC_CONFIG_ARM_64 -fPIC -shared get.c -o libndk-linux.so

CLANG

1
aarch64-linux-android21-clang -fPIC -shared hi.c -o libndk-mac.so

导入Android Studio

复制so到项目中

在app/src/main 目录下新建jniLibs目录,再新建arm64-v8a目录,将编译生成的libndk-linux.so及libndk-mac.so复制到目录下

配置cmake

在app/src/main 目录下新建cpp目录,新建CMakeLists.txt,配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59

# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html

# Sets the minimum version of CMake required to build the native library.

cmake_minimum_required(VERSION 3.10.2)

# Declares and names the project.

project("ndk")

# 包含所有CPP文件
file(GLOB allCPP *.cpp)

# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.

add_library( # Sets the name of the library.
native-lib

# Sets the library as a shared library.
SHARED

# Provides a relative path to your source file(s).
${allCPP})

# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.

find_library( # Sets the name of the path variable.
log-lib

# Specifies the name of the NDK library that
# you want CMake to locate.
log)

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -L${CMAKE_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}")

# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.

target_link_libraries( # Specifies the target library.
native-lib

# Links the target library to the log library
# included in the NDK.
${log-lib}
# 链接libndk-mac
ndk-mac
# 链接libndk-linux
ndk-linux
)

配置gradle

配置app模块下的build.gralde文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

android {

defaultConfig {
//...

externalNativeBuild {
cmake {
abiFilters "arm64-v8a"
}
}
ndk {
abiFilters "arm64-v8a"
}
}

externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
version "3.10.2"
}
}
}

引用so中的方法

在cpp目录下,新建native-lib.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <jni.h>
#include <string>
#include <android/log.h>

#define TAG "Wangyz"

#define LOG_I(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);

extern "C" int get();

extern "C" int hi();

extern "C" JNIEXPORT jstring JNICALL
Java_com_wangyz_ndk_MainActivity_stringFromJNI(
JNIEnv *env,jobject /* this */) {
int a = get();
LOG_I("hello:%d", a);

int b = hi();
LOG_I("hi:%d", b);

return env->NewStringUTF("hello");
}

Activity中调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

public class MainActivity extends AppCompatActivity {

// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("ndk-mac");
System.loadLibrary("ndk-linux");
System.loadLibrary("native-lib");
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

// Example of a call to a native method
TextView tv = findViewById(R.id.sample_text);
tv.setText(stringFromJNI());
}

/**
* A native method that is implemented by the 'native-lib' native library,
* which is packaged with this application.
*/
public native String stringFromJNI();
}