NDK 是 Android 所提供的一个开发工具集,通过 NDK 可以在 Android 中更加方便的通过 JNI 来访问本地代码,比如 C 或者 C++。
相关概念
- 什么是交叉编译?
交叉编译就是一个平台上生成另一个平台上可执行的代码。如在 x86 平台编译成可在 arm 平台运行的程序。 - JNI 是什么?
它允许 Java 代码和其他语言写的代码进行交互,交互用到这个接口进行中转。 - JNI 实现流程
编写 Java 类代码 — 编译成字节代码 — 产生 c 头文件 — 编写 JNI 实现代码 — 编译成连接库 — 运行程序
NDK 示例
-
创建 Android 工程,工程名为 HelloNDK。
-
在 MainActivity 中声明 native 方法
public static native String getStringFromC();
-
通过 javah 命令生成头文件
javah 生成 Java 类对应的头文件必须要用到 javah 命令。
-d 表示生成一个目录。
…/jni 表示在当前目录的上一层目录,当前在Java目录下,那么它的上层目录就是 main 目录。
com.example.hellondk.MainActivity 完整类名 -
实现 JNI 方法,在 jni 目录下创建 hello.c 文件,代码如下。
#include<stdio.h>
#include<stdlib.h>
#include "com_example_hellondk_MainActivity.h"
/**
* JNIEnv* 表示一个指向 JNI 环境的指针,可以通过它来访问 JNI 提供的借口方法
* JNIEXPORT JNICALL 是 JNI 所定义的宏,可以在 jni.h 头文件中找到
*/
JNIEXPORT jstring JNICALL Java_com_example_hellondk_MainActivity_getStringFromC(JNIEnv *env,jclass jclass){
return (*env) -> NewStringUTF(env,"Hello from JNI");
}
- 同样在 jni 目录下创建 Android.mk 文件
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := hello //模块名称,需要加载的 so 库文件
LOCAL_SRC_FILES := hello.c //需要参与编译的源文件
include $(BUILD_SHARED_LIBRARY)
- 可以通过 ndk-build 命令编译产生 so 库,也可以让 AndroidStudio 自动编译 JNI 生成,采用自动编译的方法。
- 在 gradle.properties 文件中最后一行添加代码
andorid.useDeprecatedNdk=true
- 在 app 目录下 build.gradle 文件中添加代码如下
defaultConfig { applicationId "com.example.hellondk" minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" ndk{ moduleName "hello" //指定打包后的 so 库文件名 abiFilters "armeabi-v7a","x86" //指定某个 CPU 平台的 so 库 } }
- 在 gradle.properties 文件中最后一行添加代码
- 最后一步加载 so 库文件,并调用方法测试。
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("hello");
}
public static native String getStringFromC();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.textView);
textView.setText(getStringFromC());
}
}
NDK 打印 log 信息
-
app 目录下 build.grade 文件 ndk 包层添加代码
ldLibs "log"
-
修改 Android.mk 文件添加 log 库,加入如下代码
LOCAL_LDLIBS += -L$(SYSROOT)/usr/lib -llog
-
在实现 JNI 方法的 .c 文件需要引入头文件,以及宏定义,完整代码如下
#include<stdio.h>
#include<stdlib.h>
#include "com_example_hellondk_MainActivity.h"
#include<android/log.h> //引入头文件
#define TAG "seeker" //
//第一个参数 log 级别,第二个参数 key,第三个参数打印信息
#define LOGV(...)__android_log_print(ANDROID_LOG_VERBOSE,TAG,__VA_ARGS__)
/**
* JNIEnv* 表示一个指向 JNI 环境的指针,可以通过它来访问 JNI 提供的接口方法
* JNIEXPORT JNICALL 是 JNI 所定义的宏,可以在 jni.h 头文件中找到
*/
JNIEXPORT jstring JNICALL Java_com_example_hellondk_MainActivity_getStringFromC(JNIEnv *env,jclass jclass){
LOGV("hello"); //打印 log 调用
return (*env) -> NewStringUTF(env,"Hello from JNI");
}