热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

修改linphonesdkandroid中篇

前言接上篇批改linphone-sdk-android-上篇本文是中篇,本篇记录问题2的后续排查过程及修复计划,尽量形容排查问题过程中的思路与方向剖析上篇说到减少日志,编译后放到A
文章目录[隐藏]
  • 前言
  • 剖析

前言

接上篇批改linphone-sdk-android-上篇

本文是中篇,本篇记录问题2的后续排查过程及修复计划,尽量形容排查问题过程中的思路与方向

剖析

上篇说到减少日志,编译后放到AS中运行,查看Logcat输入

// up不为NULL
2022-04-24 18:10:36.969 4002-4018/com.guodong.android.linphone D/guodongAndroid: up = 0x100433
2022-04-24 18:10:36.969 4002-4018/com.guodong.android.linphone D/guodongAndroid: up_available1 = 0
2022-04-24 18:10:36.969 4002-4018/com.guodong.android.linphone D/guodongAndroid: up_available2 = 0

// up为NULL
2022-04-24 18:10:39.669 4002-4018/com.guodong.android.linphone D/guodongAndroid: up = 0x0
2022-04-24 18:10:39.669 4002-4018/com.guodong.android.linphone D/guodongAndroid: up_available1 = 1
2022-04-24 18:10:39.669 4002-4018/com.guodong.android.linphone D/guodongAndroid: up_available2 = 1

从日志中能够看出,有时upNULL的,猜测有销毁的办法,再次查看linphone_jni.cc,发现有一个unref办法:

JNIEXPORT jboolean JNICALL Java_org_linphone_core_LoggingServiceImpl_unref(JNIEnv* env, jobject thiz, jlong ptr) {
    LinphoneLoggingService *cptr = (LinphoneLoggingService*)ptr;
    if (cptr == 0) {
        bctbx_error("Java_org_linphone_core_LoggingServiceImpl_unref's LinphoneLoggingService C ptr is null!");
        return TRUE;
    }

    jobject wref = (jobject)belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);
    belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, nullptr, nullptr);
    if (wref) {
        env->DeleteWeakGlobalRef(wref);
    }
    return belle_sip_object_unref_2(cptr) == 1;
}

嗯,看来这个就是销毁办法了,通过belle_sip_object_data_get办法取出值,再通过belle_sip_object_data_set办法设置为nullptr,而后删除全局弱援用

这个办法也加点日志输入吧,关上jni.mustache,找到模板办法,增加日志输入:

JNIEXPORT jboolean JNICALL Java_{{jniPrefix}}{{classImplName}}_unref(JNIEnv* env, jobject thiz, jlong ptr) {
    {{classCName}} *cptr = ({{classCName}}*)ptr;
    if (cptr == 0) {
        bctbx_error("Java_{{jniPrefix}}{{classImplName}}_unref's {{classCName}} C ptr is null!");
        return TRUE;
    }

    jobject wref = (jobject)belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);

    // begin - added
    {{#isLoggingService}}
    #ifdef __ANDROID__
    __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "unref wref = %p", wref);
    #endif /* __ANDROID__ */
    {{/isLoggingService}}
      // end - added

    belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, nullptr, nullptr);
    if (wref) {
        env->DeleteWeakGlobalRef(wref);
    }
    {{#refCountable}}return belle_sip_object_unref_2(cptr) == 1;{{/refCountable}}
    {{#notRefCountable}}return FALSE;{{/notRefCountable}}
}

从新编译后放到AS中运行,查看Logcat输入

2022-04-24 18:18:52.359 4240-4249/com.guodong.android.linphone D/guodongAndroid: unref wref = 0x1002e3
2022-04-24 18:19:02.296 4240-4257/com.guodong.android.linphone D/guodongAndroid: up = 0x0
2022-04-24 18:19:02.296 4240-4257/com.guodong.android.linphone D/guodongAndroid: up_available1 = 1
2022-04-24 18:19:02.296 4240-4257/com.guodong.android.linphone D/guodongAndroid: up_available2 = 1

从日志中能够剖析出两点:

  1. 确实有销毁的办法被调用
  2. 调用unref办法的线程与调用getLoggingService办法的线程不同

联合以上两点,大胆的猜想问题出在多线程上,在多线程上此问题是偶现的就不奇怪了

出问题时,日志输入如下

2022-04-24 18:25:02.296 4240-4257/com.guodong.android.linphone D/guodongAndroid: up = 0x20004f
2022-04-24 18:25:02.296 4240-4257/com.guodong.android.linphone D/guodongAndroid: up_available1 = 1
2022-04-24 18:25:02.296 4240-4249/com.guodong.android.linphone D/guodongAndroid: unref wref = 0x1002e3
App Crash

多线程问题,第一想法是通过加锁,保障代码间调用的互斥性

再次关上jni.mustache,增加互斥锁相干代码:

// Added by guodongAndroid on 2022/04/22
#ifdef __ANDROID__
static pthread_mutex_t mutex;
#endif /* __ANDROID__ */

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *ajvm, void *reserved) {
#ifdef __ANDROID__
    ms_set_jvm(ajvm);
    int result = pthread_mutex_init(&mutex, NULL);
    __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "JNI_OnLoad, mutex init result = %d", result);
#endif /* __ANDROID__ */
    jvm = ajvm;
    return JNI_VERSION_1_2;
}

// Added by guodongAndroid on 2022/04/22
JNIEXPORT void JNI_OnUnload(JavaVM *ajvm, void *reserved) {
#ifdef __ANDROID__
    int result = pthread_mutex_destroy(&mutex);
    __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "JNI_OnUnload, mutex destroy result = %d", result);
#endif /* __ANDROID__ */
}

{{#objects}}
JNIEXPORT jobject JNICALL get{{className}}(JNIEnv *env, {{classCName}} *cptr, bool_t takeref) {
    jobject jobj = nullptr;

    if (cptr != nullptr) {
        // begin add
        {{#isLoggingService}}
        #ifdef __ANDROID__
        pthread_mutex_lock(&mutex);
        #endif /* __ANDROID__ */
        {{/isLoggingService}}
        // end add
        
        void *up = belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);
        LinphoneJavaBindings *ljb = (LinphoneJavaBindings *)linphone_factory_get_user_data(linphone_factory_get());
        if (!ljb) {
            ljb = new LinphoneJavaBindings(env);
            linphone_factory_set_user_data(linphone_factory_get(), ljb);
        }

        jclass {{cPrefix}}_class = ljb->{{cPrefix}}_class;
        jmethodID {{cPrefix}}_cOnstructor= ljb->{{cPrefix}}_class_constructor;

        {{#isLoggingService}}
        #ifdef __ANDROID__
        __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up = %p", up);
        
        jobject temp_jobj1 = (jobject)up;
        jboolean up_available1 = env->IsSameObject(temp_jobj1, NULL);
        __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up_available1 = %d", up_available1);
        
        jobject temp_jobj2 = (jobject)up;
        jboolean up_available2 = env->IsSameObject(temp_jobj2, nullptr);
        __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "up_available2 = %d", up_available2);
        #endif /* __ANDROID__ */
        {{/isLoggingService}}

        if (up == nullptr) {
            jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);
            belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
            if (takeref)
                {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}
        } else {
            jobj = env->NewLocalRef((jobject)up);
            if (jobj == nullptr) {
                // Delete weak ref ?
                env->DeleteWeakGlobalRef((jobject)up);
                // takes implicit local ref
                jobj = env->NewObject({{cPrefix}}_class, {{cPrefix}}_constructor, (jlong)cptr);
                belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, (void*)env->NewWeakGlobalRef(jobj), nullptr);
                if (takeref)
                    {{#refCountable}}{{cPrefix}}_ref(cptr);{{/refCountable}}
            }
        }
        
        // begin add
        {{#isLoggingService}}
        pthread_mutex_unlock(&mutex);
        {{/isLoggingService}}
        // end add
    }
    return jobj;
}

JNIEXPORT jboolean JNICALL Java_{{jniPrefix}}{{classImplName}}_unref(JNIEnv* env, jobject thiz, jlong ptr) {
    {{classCName}} *cptr = ({{classCName}}*)ptr;
    if (cptr == 0) {
        bctbx_error("Java_{{jniPrefix}}{{classImplName}}_unref's {{classCName}} C ptr is null!");
        return TRUE;
    }

    // begin add
    {{#isLoggingService}}
    #ifdef __ANDROID__
    pthread_mutex_lock(&mutex);
    #endif /* __ANDROID__ */
    {{/isLoggingService}}
    // end add
    
    jobject wref = (jobject)belle_sip_object_data_get((belle_sip_object_t *)cptr, belle_sip_java_user_data_key);

    {{#isLoggingService}}
    #ifdef __ANDROID__
    __android_log_print(ANDROID_LOG_DEBUG, "guodongAndroid", "unref wref = %p", wref);
    #endif /* __ANDROID__ */
    {{/isLoggingService}}

    belle_sip_object_data_set((belle_sip_object_t *)cptr, belle_sip_java_user_data_key, nullptr, nullptr);
    if (wref) {
        env->DeleteWeakGlobalRef(wref);
    }
    
    // begin add
    {{#isLoggingService}}
    pthread_mutex_unlock(&mutex);
    {{/isLoggingService}}
    // end add
    
    {{#refCountable}}return belle_sip_object_unref_2(cptr) == 1;{{/refCountable}}
    {{#notRefCountable}}return FALSE;{{/notRefCountable}}
}

从新编译后拷贝到AS中运行,继续察看Logcat及运行状况



推荐阅读
  • 如何搭建Java开发环境并开发WinCE项目
    本文介绍了如何搭建Java开发环境并开发WinCE项目,包括搭建开发环境的步骤和获取SDK的几种方式。同时还解答了一些关于WinCE开发的常见问题。通过阅读本文,您将了解如何使用Java进行嵌入式开发,并能够顺利开发WinCE应用程序。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 开发笔记:Java是如何读取和写入浏览器Cookies的
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了Java是如何读取和写入浏览器Cookies的相关的知识,希望对你有一定的参考价值。首先我 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 如何自行分析定位SAP BSP错误
    The“BSPtag”Imentionedintheblogtitlemeansforexamplethetagchtmlb:configCelleratorbelowwhichi ... [详细]
author-avatar
mobiledu2502887287
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有