NDK入门之JNI篇

提到NDK,相信有很多想进阶Android的朋友一听到这个名字都感觉有点头痛,但是你放心,这仅仅是噩梦的开始。

要说NDK的配置,主要是为了配合android开发的cpu等硬件问题而配置的,其实这些不是重点,要记住这些都是属性配置而已。真正有问题的地方就是要编写这些JNI的C,C++部分。

JNI是Java NativeInterface,就是java跟C,C++的接口,在jdk里面就已经有存在的两个头文件。在jdk安装路径的include文件下有一个jni.h,还有一个jni_md.h。在android中,主要用jni.h就够了,也就是加入C++ support的时候,默认的.cpp文件会自动加载了jni.h。

jni.h中定义了很多java和C的基本数据类型对照表,还有定义了一些相互调用的方法。

在java中要定义C/C++的方法要加关键字native,例如:

public native void sayHello();

在C/C++中定义这个函数名字要遵循这样的格式:

JNIEXPORT JNI类型返回值 JNICALL

Java_包名_类名_方法名(JNIEnv*env,各种参数)

就上面的例子而言,就要写成

JNIEXPORT void JNICALL
Java_com_arjinmc_ndkdemo_JNIUtils_sayHello(JNIEnv*env)

觉得很麻烦,没事,java提供了一个命令可以让把这些了名生成出来,只要用端口打开到编译好的classes目录下,找到你定义native方法的这个java类,然后输入javah 类名就可以看到在jni/cpp目录下自动生成了该类的头文件.h,打开就能看到这些方法名。但是,也可以不用这么麻烦,因为Android Studio 2.2开始已经自动识别到native关键字,在这个native java方法按下alter+enter就会自动生成该方法对应的C/C++方法。但是要注意,在cpp文件中,每个方法名字前面都要加上extern “C”,这样保证兼容C的方法。

不管是Java调用C/C++,还是C/C++调用java,上面两个内容都必须包含。

JNIEnv *env 是一级指针,在C++中可以直接使用,但是要在C中使用,env在C中是二级指针,所以要写成(*env)。

以下这个表是java,jni,c/c++对应的基本数据类型。jni就充当着两个派系语言的翻译官。

Java 类型

jni 类型

实际表示的 C类型

Win32

说明

boolean

jboolean

unsigned char

无符号,8 位

byte

jbyte

signed char

有符号,8 位

char

jchar

unsigned short

无符号,16 位

short

jshort

short

有符号,16 位

int

jint

long

有符号,32 位

long

jlong

__int64

有符号,64 位

float

jfloat

float

32 位

double

jdouble

double

64 位

void

void

N/A

N/A

 

java调用C/C++主要步骤:

C/C++从java所对应过来的函数中对应了的参数都是jni的类型。因此,需要jni定义的一些方法去做java和C/C++数据类型的切换。

 

GetStringChars和GetStringUTFChars

jboolean: JNI_TRUE,JNI_FALSE

const jchar* GetStringChars(jstringstr,jboolean* copied)

会把java的string复制到新的内存,获取到该指针。

const char* GetStringUTFChars(jstringstr,jboolean* copied)

直接获取java的string的内存指针。

 

不用的时候释放内存调用ReleaseStringChars,ReleaseStringUTFChars。

 

GetStringCritical/ReleaseStringCritical

得到字符串的指针,禁止GC。如果禁止GC时被GC了,会造成阻塞,死锁。

jchar* GetStringCritical(jstring str,jboolean*copied)

 

GetStringRegion/GetStringUTFRegion

先在内存创建空间,再复制。

//utf-16

GetStringRegion(jstring str,jsizestart,jsize len,char* buffer)

//utf-8

GetStringUTFRegion(jstring str,jsizestart,jsize len,char* buffer)

 

其他字符串相关用函数

新建

jstring NewString(const jchar* str,jsizelen);

jstring NewStringUTF(const char* str);

获取字符串大小

jszie GetStringLength(jstring str);

jsize GetStringUTFLength(jstring str);

 

处理数组

基本类型

Get<Type>ArrayElements(<Type>Arrayarr,jboolean* isCopied)

Release<Type>ArrayElements(<Type>Arrayarr,<Type>* array,jint mode)

mode:

0进行更新并释放

JNI_COMMIT 进行更但不释放

JNI_ABORT 不更新,释放

 

GetPrimitiveArrayCritical(jarryarr,jboolean* isCopied)

ReleasePrimitiveArrayCritical(jarrayarr,void* array,jint mode)

得到数组的指针,禁止GC。如果禁止GC时被GC了,会造成阻塞,死锁。

 

Get<Type>ArrayRegion(<Type>Arrayarr,jsize start,jsize len,<Type>* buffer);

先在内存申请空间,在复制过去.

对象类型

Get/SetObjectArrayElement

 

C/C++调用Java的步骤:

1.获取到java的类名;

2.获取到java类中的方法名id;

3.通过env->Callxxxx调用java的方法。

 

获取java类名

 jclass FindClass(const char* clsName);

 jclass GetObjectClass(jobject obj);

 jclass GetSuperClass(jclass obj); //获取父类名

 

获取java类名中的方法id

jmethodID GetMethodID(jclass clazz,const char* name,const cahr*sign);

jmethodID GetStaticMethodID(jclass clazz,const char* name,constcahr* sign);

例:获取构构造方法

jmethodID env->GetMethodID(clazz,”<init”>,”()V”);

构造方法比较特殊,必须是<init>,其他方法直接写方法名称。最后一个参数是方法的签名,签名表如下:

类型

签名

boolean

Z

byte

B

char

C

short

S

int

I

long

L

float

F

double

D

void

V

object

L开头,完整类名用/切割,最后加上;

Array

[签名

Method

(参数1签名…参数签名n)返回值类型签名

 

调用java方法

通过env->CallXXXXMethod(jclass/jobject,jmethodID,param1,param2….)

要注意的是,调用java的方法,不能调用过于复杂的,比如返回值是数组类型,自定义类型的java方法是无法call出来。所以,可以定义成void方法,这样C/C++可以直接通过内存去修改java的参数的值。

 

还有其他可以让C/C++去修改java类的属性

获取java类属性的id

jfieldID GetFieldID(jclass clazz,const char* name,const cahr* sign);

 jfieldID GetStaticFieldID(jclass clazz,const char* name,const cahr*sign);

get/set  java类属性

Set<Type>Field

Get<Type>Field

SetStatic<Type>Field

GetStatic<Type>Field

 

JNI基本内容已经说完,剩下都是C/C++的问题,自己琢磨吧。

我写了一个Demo,有兴趣下载看看:

https://github.com/arjinmc/NDKDemo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值