开始之前
最近学习了一下NDK的开发, 就来分享一下.
对一个新鲜事物, 我们先解决的无非就是三件事情: 是什么?为什么?怎么做?.
NDK简介
(英语:native development kit,简称NDK)是一种基于原生程序接口的软件开发工具。通过此工具开发的程序直接以本地语言运行,而非虚拟机。因此只有java等基于虚拟机运行的语言的程序才会有原生开发工具包。[维基百科]
- NDK是一系列工具的集合
NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。这些工具对开发者的帮助是巨大的.
- NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
NDK可以自动地将so和Java应用一起打包,极大地减轻了开发人员的打包工作。
那我们为什么要使用呢?
- 代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反汇难度较大。
- 可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
- 提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
- 便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。
上述文字致谢Devin Zhang提供理论支持
是什么和为什么我就先介绍到这儿, 接下来就具体看看如何进行NDK的开发
开发前准备
开发环境
请大家务必升级到AS 2.2以上版本, 因为这个版本升级了很多内容, 详情请见 Android Studio 2.2 正式稳定版发布
- Android Studio 2.2.2
- JDK1.7
- API 24
- Gradle 2.2.2
NDK和CMake 的下载和安装
大家可以直接打开SDK进行下载和安装
由于NDK的工具包较大, 大家也可以选择从网站中下载: http://wear.techbrood.com/tools/sdk/ndk/, 选择自己对应的版本使用迅雷等工具下载即可, 不过通过这种方法一定要修改local.properties文件, 在里面添加:
//后面改成自己下载后解压的路径名
ndk.dir=C\:\\Users\\Lulu\\AppData\\Local\\Android\\android-ndk-r13
关于CMake
- CMakeList.txt 是脚本文件, 需要指定包含哪些源代码;
- 可以写一些条件语句, 实现不同的代码包含
- 内部说明:
add_library 表示编译一个代码库, 内部包含了代码库的名称, 以及源代码有哪些
NDK两种开发模式
- ndk-build 形式; Android Studio 2.2之前的模式
- CMake 形式: CLion C/C++编辑器; AS2.2之后整合了CLion代码, AS就支持了CMake形式的NDK开发
开始开发
接下来通过几个案例来演示NDK的开发流程
创建工程
-
新建工程, 选中Include C++ Support
-
一路Next之后, 在最后Finish页面尽量选中图示两项, 这样会给我们包裹一些特定的示例代码, 帮助我们理解和使用
-
点击Finish, 如果出现图示错误的肯定没有好好看上面的 开发前准备
案例一 实现在C语言中隐藏AppKey等信息
step1: 为了使代码整洁, 咱们新建一个类 NativeHelper, 专门用于访问C语言代码的帮助类 并添加获取Appkey的方法
public class NativeHelper {
static {
// 加载C代码库, 库的名称, 必须是CMakeLists.txt中指定的名称
System.loadLibrary("native-lib");
}
//获取C中隐藏的AppKey
public static native String getAppKey();
}
Note: 一定要添加上面的静态代码块的内容, 否则无法加载C代码库
此时的getAppKey()方法标红, 不用管它, 继续....
step2: 在cpp目录下右击创建C/C++ Source, 选择Type, 并勾选 Create an associated hader, 为保持对应, 名字命名为: com_lulu_ndkdemo_NativeHelper, 此时会出现, 在cpp目录下会出现两个文件, 如图:
step3: 在CMakeLists.txt中的add_library中添加依关系, 点击同步
src/main/cpp/com_lulu_ndkdemo_NativeHelper.c
Note:
C代码库生成的名称规则
- 如果栈顶代码库名称为 "nh" 那么生成的文件必定是libnh.so
命名规则: lib库名.so - System.loadLibrary(库名); //此处不能包含前面的lib和后面的.so
step4: 在com_lulu_ndkdemo_NativeHelper.c文件中添加c语言代码
#include <jni.h>
JNIEXPORT jstring JNICALL
Java_com_lulu_ndkdemo_NativeHelper_getAppKey(JNIEnv *env, jclass type) {
//测试代码, 没有任何意义
char* app_key = "5465465416948";
//生成 Java 中的字符串对象
//指针的指针
// env <=> JNINativeInterface** C语言
return (*env)->NewStringUTF(env, app_key);
}
step5: 在MainActivity中获取AppKey, 查看结果 -> 成功
String appKey = NativeHelper.getAppKey();
Log.d(TAG, "onCreate: appKey => " + appKey);
Note: Java 调用C/C++代码
- 任何一个类的方法, 如果声明了native修饰符, 那么就可以认为是一个C代码;
- 可以用对象, 类直接调用
- 创建C/C++文件; 如果一个类中有一个native的方法, 那么对应的C方法: Java_包名类名方法名(JNIEnv *env, ...);
- 当Java类中包含了native的方法, 那么这个类必须写一个静态初初始化块: System.loadLibrary("库名")
案例二 实现在C语言中打印log
接下来, 只简单介绍核心代码, 不再赘述
step1: 在com_lulu_ndkdemo_NativeHelper.c中添加:
JNIEXPORT void JNICALL
Java_com_lulu_ndkdemo_NativeHelper_printLog(JNIEnv *env, jclass type, jstring str_) {
const char *str = (*env)->GetStringUTFChars(env, str_, 0);
//TODO: 显示Android 的日志
// 调用Android的代码
// 代码需要调用系统的日志库, 这个库已经在 CMakeList.txt添加了e,因此可以直接调用
const char *tag = "NativeHelper";
//jstring -> char*
jboolean b = JNI_FALSE;
const char* txt = (*env)->GetStringUTFChars(env, str_, b);
//打印log日志
__android_log_write(ANDROID_LOG_DEBUG, tag, txt);
//释放string
(*env)->ReleaseStringUTFChars(env, str_, str);
}
step2: NativeHelper中添加
//在C中打印log
public static native void printLog(String str);
step3: MainActivity中调用
//打印C语言中的Log
NativeHelper.printLog("测试Log");
完整代码
Demo已上传到github上, 欢迎大家Clone
小结
至此, 咱们应该大致了解了一下NDK开发的简单流程, 鄙人菜鸟, 希望抛砖引玉, "引"出更好的文章
本文还有下篇, 将再写一些关于NDK开发的案例Demo, 希望大家喜欢和关注