个人博客
前言
记录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
wget https://dl.google.com/android/repository/android-ndk-r17c-linux-x86_64.zip
NDK18及之后的NDK版本,建议使用CLANG编译。
解压NDK
unzip android-ndk-r17c-linux-x86_64.zip
解压后得到android-ndk-r17c文件夹
编写头文件及c文件
GCC编译
#include "get.h"
int get(){
return 666;
}
#include "get.h"
int get(){
return 666;
}
CLANG编译
#include <stdio.h>
int hi();
#include "hi.h"
int hi(){
return 888;
}
配置NDK
Linux(使用GCC编译)
编辑Home/用户 目录下的.bashrc
vim /home/wangyz/.bashrc
添加以下内容
# 配置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
vim ~/.bash_profile
添加以下内容
# 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
$NDK_GCC_ARM_64 $NDK_GCC_CONFIG_ARM_64 -fPIC -shared get.c -o libndk-linux.so
CLANG
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,配置如下:
# 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文件
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
#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中调用
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();
}