Android JNI浅析、Java和Native通信对象的传值和回调
创始人
2024-05-30 17:08:10
0

简单了解一下jni

JNI是一个本地编程接口,它允许运行在Java虚拟机的Java代码与用其他语言(如C,C++和汇编)编写的库交互。

jni函数签名

首先看一下java类型对应的jni类型:

Java类型符号
BooleanZ
ByteB
CharC
ShortS
IntI
LongJ
FloatF
DoubleD
VoidV
数组[ 比如:int[] -> [I ,如果是二维数组 int[][] -> [[I
objects以"L"开头,以";"结尾,中间是用"/" 隔开的包及类名。比如:Ljava/lang/String;如果是嵌套类,则用$来表示嵌套。

比如:

privite native int test(String arg);

则它的签名为:

(Ljava/lang/String;)I

函数参数的传递

  • 基本类型(如整型,字符等)在Java和native之间是采用值传递
  • Java对象采用的是引用传递

关于局部引用的相关内容,可以参考之前的文章:
JNI内存方面说明以及相关类型手动释放内存

JavaVM和JNIEnv

这两个结构体在jni.h中有定义:

#if defined(__cplusplus)
typedef _JNIEnv JNIEnv;
typedef _JavaVM JavaVM;
#else
typedef const struct JNINativeInterface* JNIEnv;
typedef const struct JNIInvokeInterface* JavaVM;
#endif

这里也能看出c版本和c++版本,在使用调用上有一些不同,c++相当于又包了一层!

下边看一下c++的版本:
JavaVM

struct _JavaVM {const struct JNIInvokeInterface* functions;#if defined(__cplusplus)jint DestroyJavaVM(){ return functions->DestroyJavaVM(this); }jint AttachCurrentThread(JNIEnv** p_env, void* thr_args){ return functions->AttachCurrentThread(this, p_env, thr_args); }jint DetachCurrentThread(){ return functions->DetachCurrentThread(this); }jint GetEnv(void** env, jint version){ return functions->GetEnv(this, env, version); }jint AttachCurrentThreadAsDaemon(JNIEnv** p_env, void* thr_args){ return functions->AttachCurrentThreadAsDaemon(this, p_env, thr_args); }
#endif /*__cplusplus*/
};

JNIEnv

struct _JNIEnv {/* do not rename this; it does not seem to be entirely opaque */const struct JNINativeInterface* functions;#if defined(__cplusplus)jint GetVersion(){ return functions->GetVersion(this); }jclass DefineClass(const char *name, jobject loader, const jbyte* buf,jsize bufLen){ return functions->DefineClass(this, name, loader, buf, bufLen); }jclass FindClass(const char* name){ return functions->FindClass(this, name); }......
}    

总的来说:

JavaVM:是java虚拟机环境,每个进程有且只有只有一个。
JNIEnv: 是线程上下文环境,每个线程只有一个,不能跨线程。

我们可以通过JNIEnv来获取一个JavaVM:

jint GetJavaVM(JNIEnv *env, JavaVM **vm);// vm:用来存放获得的虚拟机的指针的指针。
// return:成功返回0,失败返回其他。

也可以通过JavaVM来获取一个JNIEnv:

jint GetEnv(void** env, jint version)

上边也提到了JNIEnv是线程绑定的,所以通常,用全局的JavaVM获取一个当前线程的JNIEnv的时候,通常需要绑定到当前线程:

char thread_name[128] = { 0 };
prctl(PR_GET_NAME, (char *)(thread_name));
JavaVMAttachArgs args;
args.version = JNI_VERSION_1_6;
args.name = thread_name;
args.group = NULL;
gJvm->AttachCurrentThread(&pEnv, (void *)(&args))

同样的,也需要在不使用的时候解绑:

gJvm->DetachCurrentThread();

本地函数的静态加载和动态加载

所谓静态加载,就是我们常见的,jni函数的声明是Java_包名_类名_方法名(参数...)这样子的,Java方法和本地函数之间的映射关系编译器已经帮我们做了。

下边了解一下动态加载:

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved);
JNIEXPORT void JNI_OnUnload(JavaVM* vm, void* reserved);

JNI_OnLoad方法是在动态库被加载时调用,而JNI_OnUnload则是在本地库被卸载时调用。所以这两个函数就是一个本地库最重要的两个生命周期方法。

在JNI_OnLoad()中,就可以手动注册本地函数,做好对java方法的映射。
一般的可以这个样子:

const JNINativeMethod gMethods[] = {{"native函数名", "native函数签名", (void *)native函数}, //第三个为函数指针...//别的native函数
};JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved){JNIEnv *env = NULL;jint result = -1;if (vm->GetEnv((void **) &env, JNI_VERSION_1_6) != JNI_OK) {return result;}// 动态注册native functionjclass clazz = env->FindClass("com.xx.xx.xxclass");    env->RegisterNatives(clazz, &method, 1) //第三个参数为总的数量// 返回jni的版本return JNI_VERSION_1_6;
}

访问字段和函数

对于jni函数中传入的jobject对象,要想访问对象的字段和函数,就需要先获取对应的class引用!

jobject testObject;   //假设我们已经有了这么一个对象jclass testCls = env->GetObjectClass(testObject); //获取class引用
//int testFiled;
//int test(String arg); 
jfieldID testFiledId = env->GetFieldID(testCls, "testFiledID", "I");
jmethodID testMethodId = env->GetMethodID(testCls, "test", "(Ljava/lang/String;)I");// 注意对字段的get/set,函数的调用,都必须使用具体的jobject对象,而不能是class引用
env->GetIntField(testObjcet, testFiledId);
env->CallIntMethod(testObject, testMethodId, "arg...");

java像Native中的对象的传递,一般也是安装上边的方式来进行,取出字段的值,再赋值给native对应的对象!

如果是native的对象回调到java层怎么做?

// 先找到class引用
jclass jcs = env->FindClass("com/xx/xx/xxclass");
// 创建对象有两种方法
// 第一种是调用类的构造函数:mthodId就是构造函数的id
jmethodID cls_constructor = env->GetMethodID(jcs, "", "()V");jobject NewObject(JNIEnv *env, jclass clazz, jmethodID methodID, ...);// 第二种是直接alloc对象
jobject AllocObject(jclass clazz)//然后将native对象赋值给jobject即可!

相关内容

热门资讯

诺安积极回报混合A净值下跌3.... 诺安积极回报灵活配置混合型证券投资基金(简称:诺安积极回报混合A,代码001706)公布5月15日最...
易方达先锋成长混合C净值下跌3... 易方达先锋成长混合型证券投资基金(简称:易方达先锋成长混合C,代码011892)公布5月15日最新净...
永赢数字经济智选混合发起A净值... 永赢数字经济智选混合型发起式证券投资基金(简称:永赢数字经济智选混合发起A,代码018122)公布5...
中银证券优选行业龙头混合C净值... 中银证券优选行业龙头混合型证券投资基金(简称:中银证券优选行业龙头混合C,代码009641)公布5月...
恒越成长精选混合C净值下跌3.... 恒越成长精选混合型证券投资基金(简称:恒越成长精选混合C,代码010623)公布5月15日最新净值,...
新民发现解放战争时期烈士墓碑 转自:沈阳日报  近日,新民市兴隆堡镇西高力村村民在农田里发现一烈士墓碑,烈士名字叫李义,家乡为宾县...
国泰黄金ETF联接C净值下跌3... 国泰黄金交易型开放式证券投资基金联接基金(简称:国泰黄金ETF联接C,代码004253)公布5月15...
先锋聚优A净值下跌3.42% 先锋聚优灵活配置混合型证券投资基金(简称:先锋聚优A,代码004726)公布5月15日最新净值,下跌...
财通匠心优选一年持有期混合C净... 财通匠心优选一年持有期混合型证券投资基金(简称:财通匠心优选一年持有期混合C,代码014916)公布...
“工字号”体检车进企业送健康 转自:沈阳日报  5月15日一大早,一辆崭新的大巴车就停在顺丰快递沈阳分拨中心,大巴车的车身上印着“...
易方达产业机遇混合C净值下跌3... 易方达产业机遇混合型证券投资基金(简称:易方达产业机遇混合C,代码021180)公布5月15日最新净...
华夏中证云计算与大数据主题ET... 华夏中证云计算与大数据主题交易型开放式指数证券投资基金发起式联接基金(简称:华夏中证云计算与大数据主...
东兴数字经济混合发起A净值下跌... 东兴数字经济混合型发起式证券投资基金(简称:东兴数字经济混合发起A,代码020440)公布5月15日...
国泰优势行业混合A净值下跌3.... 国泰优势行业混合型证券投资基金(简称:国泰优势行业混合A,代码005819)公布5月15日最新净值,...
南方中证全指计算机ETF净值下... 南方中证全指计算机交易型开放式指数证券投资基金(简称:南方中证全指计算机ETF,代码159586)公...
合煦智远金融科技指数(LOF)... 合煦智远国证香蜜湖金融科技指数证券投资基金(LOF)(简称:合煦智远金融科技指数(LOF)C,代码1...
信澳业绩驱动混合A净值下跌3.... 信澳业绩驱动混合型证券投资基金(简称:信澳业绩驱动混合A,代码016370)公布5月15日最新净值,...
财通景气甄选一年持有期混合C净... 财通景气甄选一年持有期混合型证券投资基金(简称:财通景气甄选一年持有期混合C,代码017491)公布...
中航机遇领航混合发起C净值下跌... 中航机遇领航混合型发起式证券投资基金(简称:中航机遇领航混合发起C,代码018957)公布5月15日...
相信中国就是相信明天   近日,国家主席习近平复信中国丹麦商会负责人,勉励中国丹麦商会及会员企业为增进中丹、中欧友好和深化...