You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

VoiceInteractor内存泄漏问题求助:Activity销毁后仍存在泄漏

VoiceInteractor 引发Activity销毁后内存泄漏问题

问题场景

Activity获取焦点后执行startLocalVoiceInteraction(Bundle.EMPTY),按下返回键销毁Activity,onLocalVoiceInteractionStopped方法已回调,但LeakCanary提示存在内存泄漏,期望Activity销毁后无内存泄漏。

LeakCanary泄漏分析结果

┬───
│ GC Root: Global variable in native code
│
├─ android.app.VoiceInteractor$2 instance
│ Leaking: UNKNOWN
│ Retaining 7.7 kB in 202 objects
│ Anonymous subclass of com.android.internal.app.
│ IVoiceInteractorCallback$Stub
│ ↓ VoiceInteractor$2.this$0
│ ~~~~~~
├─ android.app.VoiceInteractor instance
│ Leaking: UNKNOWN
│ Retaining 7.1 kB in 201 objects
│ mActivity instance of com.test.media.ui.activity.RemoteViewActivity
│ with mDestroyed = true
│ mContext instance of com.test.media.ui.activity.RemoteViewActivity
│ with mDestroyed = true
│ ↓ VoiceInteractor.mActivity
│ ~~~~~~~~~
╰→ com.test.media.ui.activity.RemoteViewActivity instance
Leaking: YES (ObjectWatcher监测到此对象是因为com.android.car.media.ui.activity.RemoteViewActivity已收到Activity#onDestroy()回调且Activity#mDestroyed为true)
​ Retaining 99.6 kB in 1639 objects
​ key = 73f5a0e0-3120-4b30-8871-630974629f74
​ watchDurationMillis = 37297
retainedDurationMillis = 32296
​ mApplication instance of com.test.media.AudioUniverseApplication
​ mBase instance of androidx.appcompat.view.ContextThemeWrapper

METADATA

Build.VERSION.SDK_INT: 33
Build.MANUFACTURER: Google
LeakCanary version: 2.12
App process name: com.test.media
Class count: 26345
Instance count: 225076
Primitive array count: 141328
Object array count: 33476
Thread count: 44
Heap total bytes: 59606146
Bitmap count: 35
Bitmap total bytes: 31128459
Large bitmap count: 0
Large bitmap total bytes: 0
Db 1: open /data/user/0/com.test.media/no_backup/androidx.work.workdb
Db 2: open /data/user/0/com.test.media/databases/leaks.db
Stats: LruCache[maxSize=3000,hits=121354,misses=229641,hitRate=34%]
RandomAccess[bytes=11694491,reads=229641,travel=74637550168,range=34466142,size=
73324879]
Analysis duration: 3654 ms

复现代码

class VoiceInteractionTestActivity: AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_remote_view)
    }

    override fun onResume() {
        super.onResume()
        if (isLocalVoiceInteractionSupported && !isVoiceInteraction) {
            startLocalVoiceInteraction(Bundle())

            lifecycleScope.launch {
                delay(1000)
                voiceInteractor.submitRequest(commandRequest)
            }
        }
    }

    private val commandRequest = object : VoiceInteractor.CommandRequest("command", Bundle()) {
        override fun onCommandResult(isCompleted: Boolean, result: Bundle?) {
            super.onCommandResult(isCompleted, result)
        }
    }
}

Activity配置

<activity
            android:name=".ui.activity.VoiceInteractionTestActivity"
            android:launchMode="singleTask" />

另一次泄漏分析结果

┬───
│ GC Root: Global variable in native code
│
├─ android.app.VoiceInteractor$2 instance
│    Leaking: UNKNOWN
│    Retaining 56.8 kB in 964 objects
│    Anonymous subclass of com.android.internal.app.
│    IVoiceInteractorCallback$Stub
│    ↓ VoiceInteractor$2.this$0
│                        ~~~~~~
├─ android.app.VoiceInteractor instance
│    Leaking: UNKNOWN
│    Retaining 56.3 kB in 963 objects
│    mActivity instance of com.test.media.ui.activity.
│    VoiceInteractionTestActivity with mDestroyed = true
│    mContext instance of com.test.media.ui.activity.
│    VoiceInteractionTestActivity with mDestroyed = true
│    ↓ VoiceInteractor.mActivity
│                      ~~~~~~~~~
╰→ com.test.media.ui.activity.VoiceInteractionTestActivity instance
​     Leaking: YES (ObjectWatcher监测到此对象是因为com.android.car.media.ui.activity.VoiceInteractionTestActivity已收到Activity#onDestroy()回调且Activity#mDestroyed为true)
​     Retaining 55.0 kB in 950 objects
​     key = 7332a37b-0bec-4d77-a377-45b18e072f06
​     watchDurationMillis = 18182
​     retainedDurationMillis = 13181
​     mApplication instance of com.test.media.AudioUniverseApplication
​     mBase instance of androidx.appcompat.view.ContextThemeWrapper

METADATA

Build.VERSION.SDK_INT: 33
Build.MANUFACTURER: Google
LeakCanary version: 2.12
App process name: com.test.media
Class count: 25489
Instance count: 221110
Primitive array count: 139050
Object array count: 33223
Thread count: 39
Heap total bytes: 58513000
Bitmap count: 28
Bitmap total bytes: 19651696
Large bitmap count: 0
Large bitmap total bytes: 0
Db 1: open /data/user/0/com.test.media/no_backup/androidx.work.workdb
Stats: LruCache[maxSize=3000,hits=120002,misses=226044,hitRate=34%]
RandomAccess[bytes=11679539,reads=226044,travel=69732047202,range=33728319,size=
72013645]
Analysis duration: 3858 ms

问题分析与解决方案

问题根源

从泄漏链可见:Native层全局变量持有VoiceInteractor$2(IVoiceInteractorCallback$Stub的匿名子类),该内部类强引用VoiceInteractor实例,而VoiceInteractor的mActivity字段仍指向已销毁的Activity,导致Activity无法被GC回收。即使onLocalVoiceInteractionStopped回调,系统内部可能存在引用清理延迟。

解决方案

  1. 主动停止语音交互并清理请求
    在ActivityonDestroy中主动调用stopLocalVoiceInteraction(),同时取消所有提交的请求,强制系统解除绑定:
override fun onDestroy() {
    super.onDestroy()
    stopLocalVoiceInteraction()
    commandRequest?.let {
        voiceInteractor?.cancelRequest(it)
    }
    commandRequest = null
}
  1. 用弱引用避免Activity被强持有
    修改CommandRequest,通过弱引用关联Activity,防止匿名内部类直接持有Activity强引用:
private val commandRequest = object : VoiceInteractor.CommandRequest("command", Bundle()) {
    private val activityWeakRef = WeakReference(this@VoiceInteractionTestActivity)
    
    override fun onCommandResult(isCompleted: Boolean, result: Bundle?) {
        super.onCommandResult(isCompleted, result)
        val activity = activityWeakRef.get() ?: return
        // 后续业务逻辑
    }
}
  1. 适配singleTask启动模式
    由于Activity使用singleTask启动模式,需在onNewIntent中检查语音交互状态,避免重复启动导致的引用残留:
override fun onNewIntent(intent: Intent?) {
    super.onNewIntent(intent)
    if (isVoiceInteraction) {
        stopLocalVoiceInteraction()
    }
}

补充说明

该问题在Android 33版本上已有不少开发者反馈,属于系统层面潜在bug。若上述方案无法彻底解决,可关注系统更新修复进展。

内容的提问来源于stack exchange,提问作者Dong Leif

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 22:48:10