内容简介:同参见之前的博客:
同 NDK
技术的渊源始于3年前,使用 so
文件的时候了解到 NDK
技术,并且 C
语言一直是强项,就鼓捣起 NDK
开发。在 AndroidStduio
还没推广的年代,基于 eclipse
搭建 NDK
开发环境需要安全依赖开发工具,并且调试起来具备难度。随后 AndroidStudio
也先后支持 nkd-build
和 cmake
使用 NDK
开发。
参见之前的博客:
1 准备工作
1.1 下载 NDK
当前 NDK 稳定版已经 发布到 r15c
。附上各个平台的下载地址:
1.2 添加NDK依赖
解压下载好的文件在本地,在 AndroidStudio 工程配置( 注意不是 AndroidStudio 工具配置 )中指定 NDK 路径。
或者在 local.properties
文件中指定NDK路径。
1.3 添加cmake支持
在 AndroidStudio 工具配置中,选择 Android SDK -> SDK Tools 中,勾选CMake并安装。
2 新建支持NDk工程
现在的 AndroidStduio
更支持一种极简方式集成 NDK
开发支持,即在下图中勾选 include C++ support
。然后选择 C++
标准。如 C++ 11
。建选默认的 ToolChain Default
。
之后正常 run 即可将 C 语言部分生成出 so 文件并打包到 apk 文件中。
3 给工程添加NDK支持
上述方式适合在新的工程中添加 NDK 支持。如何要在现有的项目中添加 NDK 支持,现提供 cmake
和 ndk-build
两种方式。
由于在同一个工程中,同时支持 cmake
和 ndk-build
两种方式编译 so 文件,因此将 C 源码单独放在 cpp-src
目录。且将 cmake
、 ndk-build
区分不同的 module
进行编译。
3.1 cmake
这是目前最受欢迎的集成方式,AndroidStduio 在创建新工程时默认使用该方式添加 NDK 支持。但在现有的工程中添加 NDK支持,需要手动配置。
创建 cmake module 添加个三个文件。
- CMakeLists.txt cmake编译配置文件
cmake_minimum_required(VERSION 3.4.1) add_library( hello-jni # so 库的名称 libhello-jni.so SHARED # 设置为分享库 # 指定C源文件的路径,指向公共cpp-src目录 ../../../../cpp-src/hello-jni.c ) find_library( log-lib # 设置路径变量名称 log # 指定CMake需要加载的NDK库 ) # 链接hello-jni库依赖的库,注意下面变量名的配置 target_link_libraries(hello-jni ${log-lib} ) 复制代码
- AndroidManifest.xml 每个module必须的配置文件,指定packageName。
<?xml version="1.0" encoding="UTF-8" ?> <manifest package="com.flueky.cmake"> </manifest> 复制代码
- Build.gradle 每个module必须的配置文件,用于构建项目。
apply plugin: 'com.android.library' android { compileSdkVersion 28 defaultConfig{ externalNativeBuild { cmake { // 指定配置参数,更多参数设置见 https://developer.android.google.cn/ndk/guides/cmake arguments "-DCMAKE_BUILD_TYPE=DEBUG" // 添加CPP标准 // cppFlags "-std=c++11" } } } externalNativeBuild { cmake { // 指定CMake编译配置文件路径 path "src/main/cpp/CMakeLists.txt" } } } 复制代码
关于 CMake 编译参数的设置,更多内容请阅读官方资料。
眼尖的小伙伴已经发现两处配置了 externalNativeBuild
。其中第二处的 externalNativeBuild
配置是生成 Gradle Task
可以不运行工程,直接在 ndk-cmake -> Tasks -> other 找到编译 so 文件有关的四个任务。
双击 exeternalNativeBuildDebug
执行任务,如图:
根据路径即可找到生成的so文件。
3.2 ndk-build
这是最传统的 ndk 编译方式。在配置得当的情况下,可以在不打开 AndroidStudio 情况下完成so文件的编译和输出。
创建 ndk-build module ,添加4个文件。
# 讲真,这个参数我看不懂。从 官方demo 抄来的。用于指定源文件的时候使用 abspath_wa = $(join $(filter %:,$(subst :,: ,$1)),$(abspath $(filter-out %:,$(subst :,: ,$1)))) # 指定当前路径 LOCAL_PATH := $(call my-dir) # 指定源文件路径 JNI_SRC_PATH := $(call abspath_wa, $(LOCAL_PATH)/../../../../cpp-src) # 声明 clear 变量 include $(CLEAR_VARS) # 指定 so 库的名称 libhello-jni.so LOCAL_MODULE := hello-jni # 指定 c 源文件 LOCAL_SRC_FILES := $(JNI_SRC_PATH)/hello-jni.c # 添加需要依赖的NDK库 LOCAL_LDLIBS := -llog -landroid # 指定为分享库 include $(BUILD_SHARED_LIBRARY) 复制代码
关于Android.mk 编译参数的设置,更多内容请阅读官方资料
# 指定编译的的so版本 APP_ABI := all # 指定 APP 平台版本。比 android:minSdkVersion 值大时,会有警告 APP_PLATFORM := android-28 复制代码
关于Application.mk 编译参数的设置,更多内容请阅读官方资料
- AndroidManifext.xml
<?xml version="1.0" encoding="UTF-8" ?> <manifest package="com.flueky.ndk"> </manifest> 复制代码
- build.gradle
apply plugin: 'com.android.library' android { compileSdkVersion 28 externalNativeBuild { ndkBuild { // 指定mk文件路径 path 'src/main/jni/Android.mk' } } defaultConfig { } } 复制代码
上面的 externalNativeBuild
作用同 CMake
方式的一样,用于编译生成 so 文件。 但是 ndk-build 还支持使用命令 ndk-build
编译 so 文件。 需要将 NDK 路径添加至环境变量。
需要在 jni
目录下执行该命令:
最后生成的so文件路径如图;
4 实践
4.1 生成头文件
在主 module 中的 MainActivity中添加 native 方法 。使用 javah 编译出头文件。 使用 -d
参数指定头文件的输出目录。
public class MainActivity extends Activity { static { // 加载 JNI 库 System.loadLibrary("hello-jni"); } ...... // 声明 Native 方法 private native String hello(); } 复制代码
在 app/src/main/java
目录下执行命令 javah
4.2 编写 C 源码
在 hello-jni.c
文件引用生成的头文件,并编写测试代码。
#include <string.h> #include <jni.h> #include "com_flueky_demo_MainActivity.h" #include "util/log.h" /** * JNI 示例,演示native方法返回一个字符串,Java 源码见 * * ndk-sample/app/src/main/java/com/flueky/demo/MainActivity.java */ JNIEXPORT jstring JNICALL Java_com_flueky_demo_MainActivity_hello( JNIEnv* env, jobject thiz ) { #if defined(__arm__) #if defined(__ARM_ARCH_7A__) #if defined(__ARM_NEON__) #if defined(__ARM_PCS_VFP) #define ABI "armeabi-v7a/NEON (hard-float)" #else #define ABI "armeabi-v7a/NEON" #endif #else #if defined(__ARM_PCS_VFP) #define ABI "armeabi-v7a (hard-float)" #else #define ABI "armeabi-v7a" #endif #endif #else #define ABI "armeabi" #endif #elif defined(__i386__) #define ABI "x86" #elif defined(__x86_64__) #define ABI "x86_64" #elif defined(__mips64) /* mips64el-* toolchain defines __mips__ too */ #define ABI "mips64" #elif defined(__mips__) #define ABI "mips" #elif defined(__aarch64__) #define ABI "arm64-v8a" #else #define ABI "unknown" #endif LOGD("日志输出示例"); return (*env)->NewStringUTF(env, "Hello from JNI ! Compiled with ABI " ABI "."); } 复制代码
4.3 运行截图
页面截图:
日志截图:
5 源码获取
工程源码已开放在GitHub, 下载地址 。如果您有多余的CSDN积分,不防从这里下载。可以直接编写 C 源码并进行调试和生成 so 文件。
Google 官方资料需要翻墙才可以阅读。想了解翻墙方法,请点 SSR 。
觉得有用?那打赏一个呗。我要打赏
此处是广告 : Flueky的技术小站
以上所述就是小编给大家介绍的《NDK开发入门终极教程》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- Go Modules 终极入门
- 人工智能研学社 · 入门组 | 《终极算法》研习第二期
- 研学社 · 入门组 | 《终极算法》第三章总结及第四章学习
- 单例终极分析(一)
- 学习的三个终极问题
- Protobuf 终极教程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。