开发者社区> 问答> 正文

jni循环调用java接口发生崩溃:报错

C本地接口

JNIEnv* jniEnv = NULL;
jclass TestProvider = NULL;
jobject mTestProvider = NULL;

void CtoJavaInit(void){

   TestProvider = (*jniEnv)->FindClass(jniEnv,"com/example/jni/C2jni");
   if(NULL == TestProvider)return 2;
   jmethodID construction_id = (*jniEnv)->GetMethodID(jniEnv, TestProvider, "<init>", "()V");
   if(0 == construction_id)return 3;
   mTestProvider = (*jniEnv)->NewObject(jniEnv, TestProvider, construction_id);
   if(NULL == mTestProvider)return 4;

   SendJ = (*jniEnv)->GetMethodID(jniEnv, TestProvider, "javaSend","([BI)I");
   if(NULL == obdSendJ)return 5;
  return 0;

}

int cSend(char *data, int datalen){
int len,intresult;
jbyteArray bytes;

if(NULL == jniEnv || NULL == mTestProvider || NULL == obdSendJ)return 0;
if(NULL == data || datalen > 128)return 0;
bytes = (*jniEnv)->NewByteArray(jniEnv, datalen);
if(NULL == bytes)return 0;
(*jniEnv)->SetByteArrayRegion(jniEnv, bytes, 0, datalen, (const jbyte *)data);
(*jniEnv)->CallIntMethod(jniEnv, mTestProvider, obdSendJ, bytes, datalen);
(*jniEnv)->DeleteLocalRef(jniEnv, bytes);
return datalen;
}

JNIEXPORT void JNICALL 

 Java_nativeMethod(JNIEnv *env, jobject obj){

   char *sendtest = "test..."; 

   jniEnv = env;

   CtoJavaInit();

   cSend(sendtest, strlen(sendtest);

}

说明:

JAVA代码循环调用nativeMethod接口,nativeMethod则通过cSend将一串数据发送到java的javaSend接口

问题是:循环发送N次后(200次左右)(*jniEnv)->CallIntMethod(jniEnv, mTestProvider, obdSendJ, bytes, datalen);出现崩溃

也做过以下测试:

nativeMethod接口中做如下修改

 char *sendtest = "test..."; 

   jniEnv = env;

   if(nativeMethod_firstcall == 1)CtoJavaInit();  //只在首次调用nativeMethod才进行接口初始化

   cSend(sendtest, strlen(sendtest);

但结果是软件更快崩溃

也试过在每次nativeMethod返回时释放mTestProvider,但依然没有效果

请大牛们指点 ...

展开
收起
kun坤 2020-06-14 10:34:54 2093 0
1 条回答
写回答
取消 提交回答
  • 引用来自“anjingyuws”的答案

    引用来自“fcsong000833”的答案

    测试了一上午发现原来不是C调用java接口出了问题,而是在java调用C接口时出问题,如下C接口

    int Java_com_example_jni_jni_jniRead( JNIEnv* env, jobject obj, jbyteArray data, jint datalen){

     jbyte *s8data; 

     s8data = (*env)->GetByteArrayElements(env,data,0);

    //以下两句存在不存在都一个样

     (*env)->DeleteLocalRef(env, s8data);

    s8data = NULL;

    return 0;

    }

    就这么个简单接口调用N(大概900多次)就崩溃,屏蔽GetByteArrayElements

    正常,请教是什么原因呢,该如何修改??

    GetByteArrayElements之后是需要ReleaseByteArrayElements的

    至于主贴中的问题,个人猜想还是因为那个init中NewObject导致局部引用表溢出崩溃的首先保证这个方法真的只调用一次(可以通过在init中输出LOG查看,或者通过引用计数之类的方法),而且对于需要在其他函数中调用的情况,还应该保证mTestProvider为GlobalRef,也就是修改NewObject为NewGlobalRef,但切记在不使用的时候DeleteGlobalRef

    这段代码看起来问题还是蛮多的:

    "SendJ = (*jniEnv)->GetMethodID(jniEnv, TestProvider, "javaSend","([BI)I");
       if(NULL == obdSendJ)return 5;
    "

    这个SendJ和下面的obdSendJ有啥关系?

    "if(nativeMethod_firstcall == 1)CtoJavaInit();"

    这里面的nativeMethod_firstcall什么时候赋值的?都怎么操作了?

    我针对你的需求把你的代码修改了一下,你看看能不能符合要求吧:

    static jobject g_testProvider = NULL;
    static jmethodID g_sendJ = NULL;
    static int g_inited = 0;

    int CtoJavaInit(JNIEnv * env)
    {
        int res = 0;
        jmethodID ctrId = NULL;
        jclass cls = (*env)->FindClass(env, "com/example/jni/C2jni");
        if (NULL == cls)
        {
            res = 2;
            goto end_func;
        }
        ctrId = (*env)->GetMethodID(env, cls, "<init>", "()V");
        if(NULL == ctrId)
        {
            res = 3;
            goto end_func;
        }
        g_testProvider = (*env)->NewGlobalRef(env, cls, ctrId);
        if(NULL == g_testProvider)
        {
            res = 4;
            goto end_func;
        }
        // 其实javaSend的参数没必要添加长度,Java中数组本身是知道自身长度的
        g_sendJ = (*env)->GetMethodID(env, cls, "javaSend","([BI)I");
        if(NULL == g_sendJ)
        {
            res = 5;
            goto end_func;
        }
    end_func:
        (*env)->DeleteLocalRef(env, cls);
        return res;
    }

    int cSend(JNIEnv * env, char * data, int datalen)
    {
        int len, intresult;
        jbyteArray bytes = NULL;
        if(NULL == g_testProvider || NULL == g_sendJ)
        {
            return 0;
        }
        if(NULL == data || datalen > 128)
        {
            return 0;
        }
        bytes = (*env)->NewByteArray(env, datalen);
        if(NULL == bytes)
        {
            return 0;
        }
        (*env)->SetByteArrayRegion(env, bytes, 0, datalen, (const jbyte *)data);
        (*env)->CallIntMethod(env, g_testProvider, g_sendJ, bytes, datalen);
        (*env)->DeleteLocalRef(env, bytes);
        return datalen;
    }

    JNIEXPORT void JNICALL Java_nativeMethod (JNIEnv * env, jclass thiz)
    {
        // 这里比较好的做法是将init和cleanup分别在Java端应用开始时和结束时调用,比如onCreate和onDestroy中调用
        char *sendtest = "test...";
        if (g_inited == 0)
        {
            CtoJavaInit(env);
            g_inited = 1;
        }
        cSend(env, sendtest, strlen(sendtest));
        CtoJavaCleanup(env);
    }

    void CtoJavaCleanup (JNIEnv * env)
    {
        if (g_testProvider != NULL)
        {
            (*env)->DeleteGlobalRef(env, g_testProvider);
            g_testProvider = NULL;
        }
    }

    不好意思,上面的代码有些错误:

    JNIEXPORT void JNICALL Java_nativeMethod (JNIEnv * env, jclass thiz)
    {
        // 这里比较好的做法是将init和cleanup分别在Java端应用开始时和结束时调用,比如onCreate和onDestroy中调用
        char *sendtest = "test...";
        if (g_inited == 0)
        {
            CtoJavaInit(env);
            g_inited = 1;
        }
        cSend(env, sendtest, strlen(sendtest));
        // 找个合适的地方调用
        // 尽量还是把初始化和资源清理单独调用的好……
        // CtoJavaCleanup(env);
    }
    ######回复 @fcsong000833 : 还有个问题忘了说,JNIEnv是针对线程有效的,所以尽量不要通过变量保存其指针,最好每次都使用JNI方法传过来的参数,如果需要在一些已经定义好的回调接口中使用,应该用JavaVM的AttachCurrentThread来获取当前线程的JNIEnv######非常感谢,虽然现在用其他方式暂时规避了这个问题,但这个代码还是很有价值。谢谢######

    问题没解决啊 , 继续请求帮忙 ....

    ######建议去iteye的高级语言虚拟机群问问######

    引用来自“南湖船老大”的答案

    建议去iteye的高级语言虚拟机群问问

    还是喜欢这里,高手啊 ....

    ######何必呢,在JE里面马上就有回答的。######

    引用来自“fcsong000833”的答案

    引用来自“南湖船老大”的答案

    建议去iteye的高级语言虚拟机群问问

    还是喜欢这里,高手啊 ....

    论会员技术水平,iteye远高于osc,毕竟那里底子厚
    ######什么错误?######

    引用来自“南湖船老大”的答案

    引用来自“fcsong000833”的答案

    引用来自“南湖船老大”的答案

    建议去iteye的高级语言虚拟机群问问

    还是喜欢这里,高手啊 ....

    论会员技术水平,iteye远高于osc,毕竟那里底子厚
    注册后还得闭嘴一天   哎 。。。
    ######

    引用来自“michaely”的答案

    什么错误?

    A: java应用

    B: java通讯接口

    C: JNI中的C接口

    C调用B

    A循环调用C,N次(200次左右)软件崩溃, 我也崩溃了 ,找不到头绪。。。

    ######

    宏哥来给你终极解决方案吧

    需要和C集成的地方, 用python.  

    python可以暴露web借口给 java。 其实lua也可以。 

    jvm是一个平台, 设计到和操作系统直接沟通的地方, 用jni就是自找麻烦。

    “ decision over convention, convention over configuration"

    这就是为什么要坚持两个凡是的原因

    ######回复 @郭煜 : android中实现http协议太简单了######用C简单实现一个http协议也是可以的,这么做过,不依赖任何库,代码量也不大,但是是单线程阻塞模型的。
    2020-06-14 10:35:01
    赞同 展开评论 打赏
问答分类:
问答地址:
问答排行榜
最热
最新

相关电子书

更多
Spring Cloud Alibaba - 重新定义 Java Cloud-Native 立即下载
The Reactive Cloud Native Arch 立即下载
JAVA开发手册1.5.0 立即下载