Ali
Ali

Reputation: 167

Jetpack compose onExit event

In a simple example, how to access the Exit event of our app, without using ViewModel or Hilt, etc.?

For example, to display a simple Toast message, while we exit the app.

The following code, when we press the back button to exit, works properly, and displays the toast:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var ctx = applicationContext
        setContent {
            checkExit(ctx)
        }
    }
}

@Composable
fun checkExit(ctx: Context) {
    DisposableEffect(""){
        onDispose {
            Toast.makeText(ctx, "onExit", Toast.LENGTH_LONG).show()
        }
    }
}

But if we minimize the app and then exit by swiping up the screen in the background, this toast will no longer be displayed

enter image description here

**Working Code, thanks to AgentP**

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        var ctx = applicationContext
        setContent {
            val lifecycle: LifecycleOwner = LocalLifecycleOwner.current
            checkExit(ctx, lifecycle)
        }
    }
}

@Composable
fun checkExit(ctx: Context, lifecycle: LifecycleOwner) {
    DisposableEffect(Unit) {
        val observer = LifecycleEventObserver { _, event ->
            when(event){
                Lifecycle.Event.ON_STOP  -> {
                    Toast.makeText(ctx, "onExit", Toast.LENGTH_SHORT).show()
                }
            }
        }
        lifecycle.lifecycle.addObserver(observer)
        onDispose {
            lifecycle.lifecycle.removeObserver(observer)
        }
    }
}

Upvotes: 5

Views: 4375

Answers (1)

AgentP
AgentP

Reputation: 7250

To understand the problem here you need to understand about Side-Effects in compose

What is DisposableEffect?

A side effect of composition that must run for any new unique value of key1 and must be reversed or cleaned up if key1 changes or if the DisposableEffect leaves the composition.

Basically, It will run

  1. when the key value you are passing to it changes or
  2. when it leaves the composition.

In this case, it is happening for a second reason i.e it is causing due to its exit from composition

But why is it working when you back press and why not working when you kill app from recent?

To understand this in a better way read onDispose callback not called when app is killed

To quote from the answer to the above question

Composer is disposed when viewLifecycle is in ON_DESTROY state. If ON_DESTROY is not dispatched, the composer will not dispose

So basically what happening is the composition will not dispose if it fails to receive the ON_DESTORY from the lifecycle

It's not safe to call anything in onDispose unless you are removing some observers and all.

How to fix this?

You can use below code

DisposableEffect(Unit) {
                    val observer = LifecycleEventObserver { lifecycleOwner, event ->
                         when(event){
                              Lifecycle.Event.ON_STOP,Lifecycle.Event.ON_DESTROY  -> {
                                  checkCtx()
                              }
                         }
                    }
                    lifecycle.addObserver(observer)
                    onDispose {
                         lifecycle.removeObserver(observer)
                    }
               }

The obve code sets an observer for lifecycle events and calls the method checkCtx() when we lifecycle gets ON_DESTROY or ON_STOP as state

checkCtx() : Note its not a composable function it should reside in activity/fragment

private fun checkCtx() {
          Toast.makeText(this, "onExit", Toast.LENGTH_LONG).show()
     }

Upvotes: 7

Related Questions