原因链路是:
切换模型时,当前代码先执行旧实例的 release(),随后立即创建新模型实例。
Duix 的 release() 只是向渲染线程发送停止消息,并没有等待线程真正结束。
旧线程稍后才执行 scrfdncnn.free(0)。
JNI 层所有数字人实例共享一个全局 g_digit 指针。
新模型覆盖全局指针后,旧渲染线程可能释放新实例;后续线程再调用 dhduix_free(NULL) 就会崩溃,因为该函数直接访问 dg->running。
正确解决方案是:
给 SDK 增加“释放并等待完成”的能力:停止旧渲染线程后执行 join(),确认线程完全退出后才能创建新模型。
在 DuixEmbeddedHost 中使用单线程队列或互斥锁,严格串行执行“停止语音 → 释放旧实例 → 等待线程退出 → 移除旧视图 → 创建新实例”。
JNI 的 free 增加空指针检查和幂等保护,并在释放前先清空全局指针。
更彻底的方案是把 JNI 的全局 g_digit 改为每个实例独立的 native handle,但改动会更大。
压测至少连续切换 50~100 次,同时覆盖播报结束后切换、页面退出和应用前后台切换。
原因链路是:
切换模型时,当前代码先执行旧实例的 release(),随后立即创建新模型实例。
Duix 的 release() 只是向渲染线程发送停止消息,并没有等待线程真正结束。
旧线程稍后才执行 scrfdncnn.free(0)。
JNI 层所有数字人实例共享一个全局 g_digit 指针。
新模型覆盖全局指针后,旧渲染线程可能释放新实例;后续线程再调用 dhduix_free(NULL) 就会崩溃,因为该函数直接访问 dg->running。
正确解决方案是:
给 SDK 增加“释放并等待完成”的能力:停止旧渲染线程后执行 join(),确认线程完全退出后才能创建新模型。
在 DuixEmbeddedHost 中使用单线程队列或互斥锁,严格串行执行“停止语音 → 释放旧实例 → 等待线程退出 → 移除旧视图 → 创建新实例”。
JNI 的 free 增加空指针检查和幂等保护,并在释放前先清空全局指针。
更彻底的方案是把 JNI 的全局 g_digit 改为每个实例独立的 native handle,但改动会更大。
压测至少连续切换 50~100 次,同时覆盖播报结束后切换、页面退出和应用前后台切换。