概述
Android NDK就是谷歌借助java JNI来调用本地native代码。具体过程依靠NDK工具与lib库,使用各平台gcc编译工具,make管理工具,交叉编译连接ndklib将c/c++编译为特定平台程序或动态连接库(linux系统不同cpu架构下的elf)。可以手写make构建生成。
而ndk-build就是提供可以在android-SDK生成的项目下,通过更加集成的makefile执行上述流程。而过去的方法中的Android.mk就是给ndk-build识别使用的。
cmake提供的makefile方式更人性化点
AS中Gradle是总得项目管理器,它调用一切(包括cmake),并将.so打包进apk中。
前备知识
- make与gcc:http://xn--74q78i15hxv3arigm4e.cn/2018/04/30/gcc%E4%B8%8Emake/
- jni与基本俩种ndk使用:http://xn--74q78i15hxv3arigm4e.cn/2018/04/20/android-NDK%E5%BC%80%E5%8F%91/
NDK详解
组件
ARM,x86,MIPS交叉编译器
构建系统
jni
各种c++的android库
目录
- build:构建系统的所有模块
- platforms:不同目标平台与android版本的头文件与库文件
- sources:可导入的现有模块
- toolchains:不同目标平台的构建工具
- ndk-build:构建用
- ndk-gdb:调试用
- ndk-stack:排错看栈用
俩个旧mk的使用
详细可参看谷歌https://developer.android.com/ndk/guides/android_mk
这里举个例子:
按行号看:
- 表示源文件的位置,my-dir宏表示当前目录下
- 清理其它LOCAL_变量,防止冲突
- 目标名字,会自动加前后缀(根据5的目标要求)
- 源文件名,可多个
- 具体构建什么,可以有:共享库,静态库,可执行程序等
ps.还可以指定各种变量用于引入库等
cmake
还是推荐查看谷歌:https://developer.android.com/ndk/guides/cmake
同时上一篇中讲的基本够了
jni:java与c/c++的数据传递
win下用javah生成的:
AS生成的:注意as下如果是cpp文件extern “C” 必加
当你使用错误修正时是生成c版本的时自动不加,当是cpp文件中错误修正时自动加
JNICALL JNIEXPORT
注意这些都在jni_md中被定义,而这个文件存在与win32文件下,因此与平台有关
stdcall:右到左压参,被调用者修改堆栈 declspec(dllexport)微软dll的导出函数
在android-ndk-r16b\sysroot\usr\include\jni.h中的是这样
JNIEnv
JNI函数表的接口指针,通过这个使用虚拟机功能,来与java进行数据交互
C:
env是指向JNINativeInterface的指针,而传入的是JNIEnv的指针,所以在c下需要解引用并将其作为环境传入。
typedef const struct JNINativeInterface_ *JNIEnv;
C与C++头文件中定义不同。
例子:
C++:
在C++中JNIEnv是C++类
例子:
jobject/jclass
这里用于访问调用该方法的对象/类的数据
实例方法传递jobject
静态方法传递jclass
extern “C”
extern “C”的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern “C”后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。
数据结构对照
jni通过提供jni数据类型让原生访问java数据类型
基本数据结构
基本数据结构可以直接通过对照接受处理返回,基本数据类型java对原生公开
java | JNI | C/C++ | 大小 |
---|---|---|---|
Boolean | Jboolean | unsigned char | 无符号8位 |
Byte | Jbyte | char | 有符号8位 |
Char | Jchar | unsigned short | 无符号16位 |
Short | Jshort | short | 有符号16位 |
Int | Jint | int | 有符号32位 |
Long | Jlong | long long | 有符号64位 |
Float | Jfloat | float | 32位 |
Double | Jdouble | double | 64位 |
引用数据类型
引用数据类型不对原生公开。
各类(jclass,jstring,jxxxx)都是typedef jobject jxxxx
各类数组(jxxxArray)都是typedef jarry jxxxArray
jarry表示一般对象数组,typedef jobject jarry
对引用数据类型的操作
基本数据结构可以直接通过对照接受处理返回
引用类型不透明,必须通过JNIEnv使用引用类型。
谨记java引用对象一直都归虚拟机中管理
字符串
C->java
java->c
释放
获得的c字串需要原生中释放(因为在堆中分配的)
数组
创建java数组
java->c副本与c副本->java
c对java数组的直接操作
同时还提供了对NIO的操作
以上为特殊化常用函数
以下为一般化函数
对象的域
1.获取jclass
2.获取域id
3.获取域
ps:这些方法都是回到java中去获得数据,导致性能下降,建议提前传好。
调用方法
1.获取方法id
方法id可以被缓存,给予多个线程使用
2.调用方法
传参只要加后面就行
ps:回调java方法开销大,慎用。
异常
c中捕获java方法中抛出的异常
c中抛出java异常
局部与全局引用
虚拟机通过追踪实例的引用决定释放对象的时机,在原生函数中通过显示的方式决定何时释放获取的引用。以下为原生通过env获得引用的种类
局部引用
局部引用的使用期限仅限于这个原生方法内(局部变量不会传到调用的函数里,函数调用栈的结构是一次函数调用产生一部分局部变量,但算在外层函数的执行期间,所以仍存在)不可缓存重用,返回后将释放。如FindClass.
手动释放:env->DeleteLocalRef(引用对象);
全局引用
全局引用在原生后续调用过程中依然有效,可以被其它原生函数与原生线程使用,只能手动释放
用env->NewGlobalRef(引用)将局部引用升级为全局引用,返回的是同类型的引用
但记得局部的也要及时释放
env->DeleteGlobalRef(引用)释放全局引用
弱全局引用
弱引用不阻止潜在的java对象被回收
env->NewWeakGlobalRef(引用)将局部引用升级为弱引用
JNI_FALSE a=env->IsSameObject(引用,NULL)用来检验弱引用是否存在
env->DeleteWeakGlobalRef(引用)释放弱引用
线程
局部引用不能被多线程共享。只有全局可以。
JNIEnv只能在方法的线程中使用,不可以被别的线程缓存使用(参数也是存于栈中的,不同线程各自维护一个栈,所以不可使用)
jni利用java对象同步
原生线程开启与java沟通