NDK开发入门终极教程
0 前言
同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,下载地址。 可以直接编写 C 源码并进行调试和生成 so 文件。 Google 官方资料需要翻墙才可以阅读。想了解翻墙方法,请点SSR。
NDK开发入门终极教程的更多相关文章
- 《Office 365开发入门指南教程》正式上线,限时优惠和邀请分享推广
我很高兴地通知大家,<Office 365 开发入门指南教程>已经正式在网易云课堂上线,你可以通过直接访问 https://aka.ms/office365devlesson 这个短地址 ...
- [转载]HTML5开发入门经典教程和案例合集(含视频教程)
http://www.iteye.com/topic/1132555 HTML5作为下一代网页语言,对Web开发者而言,是一门必修课.本文档收集了多个HTML5经典技术文档(HTML5入门资料.经典) ...
- Android NDK开发入门实例
AndroidNDK是能使Android应用开发者把从c/c++编译而来的本地代码嵌入到应用包中的一系列工具的组合. 注意: AndroidNDK只能用于Android1.5及以上版本中. I. An ...
- android studio ndk 开发入门
ndk 开发没什么神秘的ndk 说白了就是一个交叉编译的工具链,用它来生成各个CPU架构下的静态或动态链接库,linux 下就是 .a 和 .so 文件.原理就是 java 通过 JNI 和 C.C+ ...
- windows下用ADT进行android NDK开发的具体教程(从环境搭建、配置到编译全过程)
郑重申明:如需转载本博客,请注明出处,谢谢! 这几天在学习android NDK的开发.那么首先让我们来看看android NDK开发的本质是什么. NDK(Native Development Ki ...
- HTML5开发入门经典教程和案例合集(含视频教程)
HTML5作为下一代网页语言,对Web开发者而言,是一门必修课.本文档收集了多个HTML5经典技术文档(HTML5入门资料.经典)以及游戏开发案例以及教学视频等,帮助同学们掌握这门重要的技术. 资源名 ...
- 【转】Android NDK开发入门实例
写这个,目的就是记录一下我自己的NDK是怎么入门的.便于以后查看,而不会忘了又用搜索引擎一顿乱搜.然后希望能够帮助刚学的人入门. 先转一段别人说的话:“NDK全称:Native Development ...
- Android studio如何导出.so库(NDK开发入门)
转自:http://blog.csdn.net/ssy_neo/article/details/51758687 项目中用到了硬件调试,google一下拿到了硬件调试的源码,可惜握草so库根本加载不进 ...
- Android Studio NDK开发入门
从Android Studio 1.3 Beta1开始,就支持了NDK,我目前使用的版本是1.5.首先强调几点. 1.必须安装NDK并配置好环境变量(和配置JDK环境变量如出一辙:新建NDK_HOME ...
随机推荐
- Python 列表切片陷阱:引用、复制与深复制
Python 列表的切片和赋值操作很基础,之前也遇到过一些坑,以为自己很懂了.但今天刷 Codewars 时发现了一个更大的坑,故在此记录. Python 列表赋值:复制"值"还是 ...
- Linux多线程编程,为什么要使用线程,使用线程的理由和优点等
线程?为什么有了进程还需要线程呢,他们有什么区别?使用线程有什么优势呢?还有多线程编程的一些细节问题,(http://www.0830120.com)如线程之间怎样同步.互斥,这些东西将在本文中介绍. ...
- 打包ideaUI本地项目,以供本地使用
#首先我们要在本机进行一些配置 在本机配置环境变量(控制面板->高级系统设置->环境变量->) #用cmd检测是否配置成功 如果你在ideaUI里,配置好了之后.我们现在来打架包 # ...
- 业务线B/C端业务组件总结
/** * 业务线组件总结 * */ /* B端组件的总结 1.组件cssBase的总结 1像素底部边框 */ @mixin border - 1px - b($background: $gray - ...
- C#代码总结03---通过获取类型,分类对前台页面的控件进行赋值操作
该方法: 一般用于将数据库中的基本信息字段显示到前台页面对应的字段控件中 private void InitViewZc(XxEntity model) { foreach (var info in ...
- [Python]CentOS - ImportError: No module named '_curses'
网上搜了不少答案, 基本都是说Windows环境下curses包不适用的问题. 作为碰到这个问题的linux用户,实在感到无奈. 起因是在CentOS上部署uwsgi,想要使用uwsgitop来监控. ...
- APM和PIX飞控日志分析入门贴
我们在飞行中,经常会碰到各种各样的问题,经常有模友很纳闷,为什么我的飞机会这样那样的问题,为什么我的飞机会炸机,各种问题得不到答案是一件非常不爽的问题,在APM和PIX飞控中,都有记录我们整个飞行过程 ...
- go http
先看一个简单的 tcp 连接: // server ln, err := net.Listen("tcp", ":8000") if err != nil {} ...
- Java课堂笔记(一):Java基础
本篇博客将对Java中的数据类型.操作符,常量与变量和数组进行介绍.这些内容都是Java中最基本的知识,也是初学Java时最开始就需要了解的东西. Java数据类型 Java是一种强类型的语言,这就意 ...
- spring boot 集成 Redis
前提:你已经安装了Redis 1.创建一个spring boot 工程 2.pom 引入依赖:spring-boot-starter-data-redis <dependency> < ...