下载:
NKD-build在Android studio3.0版本以后就不能再用了,必须使用CMke
Settings -> Appearance & Behavior -> System Settings -> Android SDK ->SDK Tools 勾选 NDK,CMake,LLDB
配置工具:
Settings ->tools->External tools
javah
Program: J D K P a t h JDKPath JDKPath\bin\javah
Arugments:-d …/jni -jni F i l e C l a s s FileClass FileClass
Working directory: S o u r c e p a t h E n t r y SourcepathEntry SourcepathEntry…\java
ndk-build
Program:D:\adt\sdk\ndk-bundle\ndk-build.cmd
Parameters:什么都不用
Working directory: M o d u l e F i l e D i r ModuleFileDir ModuleFileDir\src\main
项目添加:
local.properties:
ndk.dir=D:\tools\adt-bundle-windows-x86_64-20140702\sdk\ndk-bundle
gradle.properties
android.deprecatedNdkCompileLease=1557488036683
app目录下build.gradle:
android{
defaultConfig{
externalNativeBuild {
cmake {
cppFlags "-std=c++11 -frtti -fexceptions"// -DLinux
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
ndk {
abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64"
}
}
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
}
Cmake语法
a. 添加包含头文件路径
include_directories(src/main/cpp/include/)
b. add_library()命令用于向CMake添加依赖源文件或库
add_library( # 生成函数库的名称,即libnative-lib.so或libnative-lib.a
native-lib #库名字
# 生成库类型:动态库为SHARED,静态库为STATIC
SHARED
# 依赖的c/cpp文件(相对路径)
src/main/cpp/native-lib.cpp )
c. find_library()命令用于定位NDK中的库
find_library( # Defines the name of the path variable that stores the
#库变量名称
log-lib
# 即在ndk开发包中查询liblog.so函数库,将其路径赋值给log-lib
log ) #库名称
d.target_link_libraries()命令用于指定要关联到的原生库的库
target_link_libraries( # 指定目标库,与上面指定的函数库名一致
native-lib
# 链接的库,根据log-lib变量对应liblog.so函数库
${log-lib} )
e.#设置生成的so动态库最后输出的路径
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${PROJECT_SOURCE_DIR}/../libs/${ANDROID_ABI})
f. 设置clag
add_definitions("-Wall -lpthread -g")
JNI原理
- JNIEnv 和 JavaVM
JavaVM: 是虚拟机在 JNI 层的代表。一个进程只有一个 JavaVM。所有的线程共用一个 JavaVM。
JNIEnv: 表示 Java 调用 native 语言的环境,封装了几乎全部 JNI 方法的指针。JNIEnv 只在创建它的线程生效,不能跨线程传递,不同线程的 JNIEnv 彼此独立。
在 native 环境下创建的线程,要想和 java 通信,即需要获取一个 JNIEnv 对象。我们通过 AttachCurrentThread 和 DetachCurrentThread 方法将 native 的线程与 JavaVM 关联和解除关联。 - JNI 中全局引用和局部引用的区别
全局引用: NewGlobalRef 和 DeleteGlobalRef 方法创建和释放一个全局引用, 全局引用能在多个线程中被使用,且不会被 GC 回收,只能手动释放。可以用于线程间共享内存数据。
局部引用: NewLocalRef 和 DeleteLocalRef 方法创建和释放一个局部引用,局部引用只在创建它的 native 方法中有效,包括其调用的其它函数中有效。因此我们不能寄望于将一个局部引用直接保存在全局变量中下次使用。不用删除局部引用,它们会在 native 方法返回时全部自动释放,但是建议对于不再使用的局部引用手动释放,避免内存过度使用。
弱全局引用:NewWeakGlobalRef 和 DeleteWeakGlobalRef 创建和释放一个弱全局引用。弱全局引用类似于全局引用,唯一的区别是它不会阻止被 GC 回收 - JNI函数的注册过程
Dalvik虚拟机在调用一个成员函数的时候,如果发现该成员函数是一个JNI方法,那么就会直接跳到它的地址去执行。也就是说,JNI方法是直接在本地操作系统上执行的,而不是由Dalvik虚拟机解释器执行。
结构体JNIEnv的成员变量functions指向的是一个函数表,这个函数表又包含了一系列的函数指针,指向了在当前进程中运行的Dalvik虚拟机中定义的函数。对于结构体JNIEnv的成员函数RegisterNatives来说,它就是通过调用这个函数表中名称为RegisterNatives的函数指针来注册参数gMethods所描述的JNI方法的