在JNI编程入门(一)当中,这种编写JNI的方法是静态注册,如果仔细研究会发现有以下问题:
- JNI 方法名字必须遵循规则且名字很长,可以看到java与C++函数之间的关系,不安全。
- JNI接口的头文件需要用户手动javah生成,非常不方便
- 程序运行效率低,因为初次调用native函数时需要根据根据函数名在JNI层中搜索对应的本地函数,然后建立对应关系,这个过程比较耗时
所以有没有更专业、通用的方法呢?
有,那就是动态注册,如果阅读Android源代码,会经常发现这种调用方式。
动态注册:
原理:利用 RegisterNatives方法,将java方法与JNI函数注册为一一对应关系。
现在将JNI编程入门(一)中的例子进行改写:
注意: 该native库在第一次被加载时,会调用JNI_OnLoad方法,在这个方法中,我们注册native函数,将java函数与C++函数进行对应映射。
在一个APP中,只有一个JavaVM,除非该APK进程死亡,否则JavaVM依然有效,而不同线程中JNIEnv则不相同,因此,JavaVM在第一使用时,可以保存下来,接下来在不同的线程环境中,通过该JavaVM可以提取到JNIEnv。
运行结果:
接下来给大家介绍下编译脚本:Android.mk, Application.mk
Android.mk文件位于项目jni/目录的子目录中,用于向构建系统描述源文件和共享库。定义 Application.mk、构建系统和环境变量所未定义的项目范围设置。
Android.mk 的语法用于将源文件分组为模块。模块是静态库、共享库或独立可执行文件。
LOCAL_PATH:=$(call my-dir)
LOCAL_PATH:表示源文件在开发树种的位置。构建系统提供的宏函数 my-dir 将返回当前目录(包含 Android.mk 文件本身的目录)
Include $(CLEAR_VARS):
CLEAR_VARS 指向特殊 GNU Makefile,在描述每个模块之前,必须声明此变量
LOCAL_MODULE:=native
LOCAL_MODULE 存储要构建的模块的名称。构建系统在生存最终共享库文件时,会添加lib前缀和.so后缀,像这个工程,最终会生成libnative.so
LOCAL_C_INCLUDES 指定相对于 NDK root 目录的路径列表,在编译所有源文件时添加到 include 搜索路径
例如: LOCAL_C_INCLUDES := xx/xx
LOCAL_SRC_FILES:= NativeClass.cpp
LOCAL_SRC_FILES 包含要构建到模块中的 C 或 C++ 源文件列表
LOCAL_CFLAGS 为构建系统设置在构建 C 和 C++ 源文件时要传递的编译器标志
LOCAL_STATIC_LIBRARIES 存储当前模块依赖的静态模块列表
LOCAL_SHARED_LIBRARIES 此模块在运行时依赖的共享库模块列表
LOCAL_LDILIBS 包含在构建共享库或可执行文件时要使用的其他链接器标志列表
BUILD_SHARED_LIBRARY 帮助系统将所有内容连接到一起,此处生成共享so库
Application.mk
APP_ABI
可以使用 APP_ABI 选择不同的 ABI。默认情况下,NDK 构建系统为 armeabi ABI 生成机器代码。
APP_PLATFORM
包含目标 Android 平台的名称。
NDK_TOOLCHAIN_VERSION
选择 GCC 编译器的版本
ndk-build 文件为一个 shell 脚本,用途是调用正确的 NDK 构建脚本