Android ExoPlayer在应用内更新后重启启动Activity时为何会启动两次?
兄弟,这种情况我之前做应用内更新的时候也碰到过,给你拆解下问题根源和解决办法~
首先说为什么会出现这个怪事:
你提到Activity本身只收到一次ON_START/ON_RESUME,但ExoPlayer的生命周期观察者却收到了两次,核心原因大概率出在Compose的重组导致LifecycleObserver被重复注册,再加上应用内更新后系统启动应用的特殊流程,把这个问题放大了。
具体来说,应用内更新完成后,系统重启你的应用时,会触发一次额外的重组(比如系统会重新初始化应用的一些全局状态),如果你的ExoPlayer是在可组合函数中直接注册LifecycleObserver,却没用到remember或者DisposableEffect这类能控制生命周期的API来限制注册次数,就会导致每次重组都注册一次观察者,自然就会收到多次回调,进而触发ExoPlayer重复执行启动逻辑,视频播到一半就重启了。
另外还有个小概率情况:应用内更新后,系统会先短暂创建一个应用实例来完成更新后的清理工作,然后再启动正式的实例,但你明确说Activity只启动一次,所以这个可能性可以先排除,重点还是看Compose的重组问题。
接下来是实打实的解决办法,给你三个具体的方案:
用
remember+DisposableEffect管好ExoPlayer的生命周期绑定
在Compose中,一定要用remember来缓存ExoPlayer实例,避免每次重组都创建新的实例;然后用DisposableEffect来绑定和解绑LifecycleObserver,确保观察者只会被注册一次:val context = LocalContext.current val exoPlayer = remember { ExoPlayer.Builder(context).build().apply { // 这里初始化你的ExoPlayer配置,比如设置媒体源 } } val lifecycleOwner = LocalLifecycleOwner.current DisposableEffect(lifecycleOwner) { val observer = object : DefaultLifecycleObserver { override fun onStart(owner: LifecycleOwner) { exoPlayer.play() } override fun onStop(owner: LifecycleOwner) { exoPlayer.pause() } } lifecycleOwner.lifecycle.addObserver(observer) // 可组合函数销毁时自动解绑观察者+释放ExoPlayer onDispose { lifecycleOwner.lifecycle.removeObserver(observer) exoPlayer.release() } }用
DisposableEffect的好处是,只有当lifecycleOwner变化时才会重新执行,而且在可组合函数销毁时会自动清理,既避免了内存泄漏,也防止了观察者重复注册。给播放状态加个标记,避免重复启动
你可以给ExoPlayer维护一个播放状态标记,比如用remember { mutableStateOf(false) }来记录是否已经开始播放,在onStart回调中先判断这个标记,只有当标记为false时才执行播放逻辑,播放后把标记设为true;在onStop时把标记重置为false,这样即使收到多次onStart回调,也不会重复启动播放:val isPlaying = remember { mutableStateOf(false) } // 在LifecycleObserver的回调中: override fun onStart(owner: LifecycleOwner) { if (!isPlaying.value) { exoPlayer.play() isPlaying.value = true } } override fun onStop(owner: LifecycleOwner) { exoPlayer.pause() isPlaying.value = false }检查应用内更新的启动参数
如果你用的是Google Play Core或者其他更新库,有些库在更新完成后重启应用时,会传递特殊的Intent参数,你可以在Activity的onCreate中检查这些参数,比如判断是否是更新后的首次启动,如果是的话,延迟个几百毫秒再初始化ExoPlayer,避免和系统的启动流程冲突。
最后给你个小排查技巧:在Logcat中过滤你的ExoPlayer相关日志,看看每次ON_START回调对应的ExoPlayer实例是不是同一个,如果是不同的实例,那就是你没做好实例缓存(没加remember);如果是同一个实例,那就是观察者重复注册或者状态判断的问题。
内容来源于stack exchange

