androidandroid-jetpack-composeandroid-lifecycleexoplayer

Why does Android ExoPlayer get started twice when my activity is re-launched after an in-app update?


When my Compose-based launch activity is run, it displays a video using ExoPlayer. This works fine, but when it happens after an in-app update the video starts playing briefly then restarts from the beginning.

I put Logcat messages in the the lifecycle event observer for my Exoplayer and see these events when launching from the Home Screen.

ON_START
ON_RESUME

But these are the events I see when launched after an in-app update.

ON_START
ON_RESUME
ON_START
ON_RESUME

Is this expected behavior? And if so, how am I supposed to compensate for this in my Compose UI?

Note that the launch activity itself isn't being started twice in either scenario. The launch activity only ever receives ON_START and ON_RESUME.


Solution

  • When in-app update completed, Android relaunches your app process via an internal mechanism that sometimes re-delivers lifecycle events, especially if you're navigating back from the Play Store update flow or using AppUpdateManager (from Play Core).

    So, In this case

    1. does not destroy and recreate your activity.

    2. Instead, it reinitializes parts of the lifecycle, triggering ON_START and ON_RESUME again.

    3. Your activity stays the same, but observers tied to the lifecycle may re-fire.

    I am assuming that you're starting the player inside DisposableEffect which is tied up with lifecycleOwner so you can add additional check

    //Initiallay

    LaunchedEffect(Unit) {
        player.prepare()
        player.playWhenReady = true
    }
    

    Then added a additional check with started state. so when app relaunches after updated based on state it will execute the code

    
    var started by rememberSaveable { mutableStateOf(false) }
    
    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START && !started) {
                player.prepare()
                player.play()
                started = true
            }
        }
        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }