JNI(Java Native Interface)Java本地接口,是为方便java调用C或者C++等本地代码所封装的一层接口。
NDK(Native Development Kit)本地开发工具链,是android提供的一个工具合集,帮助开发者快速开发C(或C++)的动态库,并能自动将.so和java应用一起打包成apk。NDK集成了交叉编译器(交叉编译器需要UNIX或LINUX系统环境),并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出.so。
ABI(Application binary interface)应用程序二进制接口。不同的CPU 与指令集的每种组合都有定义的 ABI (应用程序二进制接口),一段程序只有遵循这个接口规范才能在该 CPU 上运行,所以同样的程序代码为了兼容多个不同的CPU,需要为不同的 ABI 构建不同的库文件。当然对于CPU来说,不同的架构并不意味着一定互不兼容。
JavaVM 是虚拟机在 JNI 层的代表,一个进程只有一个 JavaVM,所有的线程共用一个 JavaVM。
JNIEnv 表示 Java 调用 native 语言的环境,是一个封装了几乎全部 JNI 方法的指针。
JNIEnv 只在创建它的线程生效,不能跨线程传递,不同线程的 JNIEnv 彼此独立。
native 环境中创建的线程,如果需要访问 JNI,必须要调用 AttachCurrentThread 关联,并使用 DetachCurrentThread 解除链接。
1)JavaVM:能够跨越线程,能够跨越函数;
2)JNIEnv:不能跨越线程,否则奔溃,可以跨越函数;
3)jobject:不能跨越线程,否则奔溃,不能跨越函数,否则奔溃。
静态注册
Java + 包名 + 类名 + 函数名 _ 拼接
// 以 c 的方式编译 JNIEXPORT jni.h 指定可见性 函数是否在导出表中 jstring 指定返回值
extern "C" JNIEXPORT jstring JNICALL
参数
JNIEnv* env, // jni 环境 实现 c 层调用 java / java 层调用 c 或 数据类型转换 jobject /* this */, // 该函数被哪个对象调用 如果是 jclass 则静态函数调用 jstring a) {
std::string hello = "Hello from C++"; return env->NewStringUTF(hello.c_str());
// NewStringUTF jni函数 系统函数 }
动态注册
JNI_OnLoad方式 当Android的VM执行到C组件(*so)里的System.loadLibrary()函数时,会产生一个Load事件,接着会去执行C组件里的JNI_OnLoad()函数。
JNI_OnLoad 函数中会执行两个操作:
1)告诉java VM此C组件使用哪一个JNI版本。
2)JNI_OnLoad()来获取JNIEnv。JNIEnv代表java环境,通过JNIEnv指针就可以对java端的代码进行操作。
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, void *reserved){ JNIEnv *env = nullptr; // JNI_OK = 0 注册成功 if(vm->GetEnv((void **) &env,JNI_VERSION_1_6) != JNI_OK){ LOGD("GetEnv failed"); return -1; } }
1、首先进行重新 JNI_OnLoad函数,在函数中进行获取vm的JNIEnv属性信息 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK) { return -1; } 2、其次 通过FindClas 找到对应的本地类 clazz = env->FindClass(className); 3、再接下来通过RegisterNatives 来注册类中的方法 if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) { return JNI_FALSE; }