Handle Android Lifecycle methods inside composable function

Handle Android Lifecycle methods inside composable function

Android Lifecycle

LifeCycle is a class from androidx.lifecycle package which helps us to get the information and observe on the lifecycle state of components like Activities and fragments

Two things to consider

1.State : Created,Started,Resumed,Destroyed and Initialized

2.Event : ON_CREATE , ON_START, ON_RESUME,ON_PAUSE, ON_STOP and ON_DESTROY

As the legend diagram follows to explain the same

Diagram of the App Lifecycle

In Activities and fragments , there are methods expose from Activity() and Fragment() class to observe these lifecycle events to meet our requirements in UI. But in compose , there are no such methods to observe the lifecycle states and events which is in built with composable . In the following ways we can get to know the lifecycle state and events.

LifeCycle Library

androidx.lifecycle - includes APIs to observe and know the lifecycle state.

Get Lifecycle State with Flows

  • Property from lifecycle which provides Lifecycle.State as a kotlin state flow

  • collect this flow as state

  • we can read these states during UI composition

There are 2 ways to do this

1.currentStateFlow

  • this is from lifecycle-common module
 val stateFlow = LocalLifecycleOwner.current.lifecycle.currentStateFlow
 val currentLifecycleState by stateFlow.collectAsState()

 Log.d("LifecycleScreen", "Lifecycle state: ${currentLifecycleState.name}")
     /*
     *  When launched ->
     *                  LifecycleScreen: RESUMED
     *  When Back button tapped ->
     *                  Lifecycle state: STARTED
     *  When Home button tapped ->
     *                  Lifecycle state: STARTED
     *                  Lifecycle state: CREATED
     * When recent button tapped ->
     *                  Lifecycle state: STARTED
     *                  Lifecycle state: CREATED
     *
     */

2.currentStateAsState

  • lifecycle-runtime-compose - provides easy way to read current lifecycle state .
val currentLifecycleState = LocalLifecycleOwner.current.lifecycle
                 .currentStateAsState()
Log.d("LifecycleScreen", "Lifecycle state: ${currentLifecycleState.value}")
    /*
     *  When launched ->
     *                  LifecycleScreen: RESUMED
     *  When Back button tapped ->
     *                  Lifecycle state: STARTED
     *  When Home button tapped ->
     *                  Lifecycle state: STARTED
     *                  Lifecycle state: CREATED
     * When recent button tapped ->
     *                  Lifecycle state: STARTED
     *                  Lifecycle state: CREATED
     *
     */

LifecycleEventEffect

  • allows us to run a block of code when certain Lifecycle.Event occurs
@Composable
fun LifecycleScreen() {

    val textValue = remember { mutableStateOf("") }

    Text(
        textValue.value,
        fontSize = 32.sp,
        modifier = Modifier.padding(32.dp)
    )

    LifecycleEventEffect(Lifecycle.Event.ON_CREATE) {
        Log.d("LifecycleScreen", "LifecycleScreen: The current state is ON_CREATE")
    }

    LifecycleEventEffect(Lifecycle.Event.ON_START) {
        Log.d("LifecycleScreen", "LifecycleScreen: The current state is ON_START")
    }

    LifecycleEventEffect(Lifecycle.Event.ON_RESUME) {
        Log.d("LifecycleScreen", "LifecycleScreen: The current state is ON_RESUME")
        textValue.value = "ON_RESUME"
    }

    LifecycleEventEffect(Lifecycle.Event.ON_PAUSE) {
        Log.d("LifecycleScreen", "LifecycleScreen: The current state is ON_PAUSE")
        textValue.value = "ON_PAUSE"
    }

    LifecycleEventEffect(Lifecycle.Event.ON_STOP) {
        Log.d("LifecycleScreen", "LifecycleScreen: The current state is ON_STOP")
    }

}

The logs are as follows

1.when the above compose is launched for first time ,

LifecycleScreen D LifecycleScreen: The current state is ON_CREATE

LifecycleScreen D LifecycleScreen: The current state is ON_START

LifecycleScreen D LifecycleScreen: The current state is ON_RESUME

2.When back button is pressed

LifecycleScreen D LifecycleScreen: The current state is ON_PAUSE

LifecycleScreen D LifecycleScreen: The current state is ON_STOP

3.When Home button is pressed

LifecycleScreen D LifecycleScreen: The current state is ON_PAUSE

LifecycleScreen D LifecycleScreen: The current state is ON_STOP

4.When Recent button is pressed

LifecycleScreen D LifecycleScreen: The current state is ON_PAUSE

LifecycleScreen D LifecycleScreen: The current state is ON_STOP

Whwn you add the following code and run the app

  LifecycleEventEffect(Lifecycle.Event.ON_DESTROY) {
        Log.d(
            "LifecycleScreen",
            "LifecycleScreen: The current state is ON_DESTROY"
        )
    }

we will get this crash report

java.lang.IllegalArgumentException: LifecycleEventEffect cannot be used to listen for Lifecycle.Event.ON_DESTROY, since Compose disposes of the composition before ON_DESTROY observers are invoked.

LifecycleStartEffect&LifecycleResumeEffect

  • similar to LifecycleEventEffect but it runs only on ON_Start and ON_Resume Event

  • It takes key that behaves like any other compose keys i.e when key changes , it triggers the code block to run again

  • when the ON_Stop event triggered , it execute the onStopOrDispose block which helps us to clean up / free resource when this lifecycle event is triggered

  • when the ON_Pause event triggered , it execute the onPauseOrDispose block

@Composable
fun LifecycleScreen() {

    LifecycleStartEffect() {
        Log.d(
            "LifecycleScreen",
            "LifecycleScreen: LifecycleStartEffect"
        )
        onStopOrDispose {
            Log.d(
                "LifecycleScreen",
                "LifecycleScreen: onStopOrDispose"
            )
        }
    }

    LifecycleResumeEffect() {
        Log.d(
            "LifecycleScreen",
            "LifecycleScreen: LifecycleResumeEffect"
        )
        onPauseOrDispose {
            Log.d(
                "LifecycleScreen",
                "LifecycleScreen: onPauseOrDispose"
            )
        }
    }
}

The Logs are as folows

1.When App launched for first time

LifecycleScreen D LifecycleScreen: LifecycleStartEffect

LifecycleScreen D LifecycleScreen: LifecycleResumeEffect

2.When Home Button is tapped

LifecycleScreen D LifecycleScreen: onPauseOrDispose

LifecycleScreen D LifecycleScreen: onStopOrDispose

3.When Recent button is tapped

LifecycleScreen D LifecycleScreen: onPauseOrDispose

LifecycleScreen D LifecycleScreen: onStopOrDispose

4.When back button is tapped

LifecycleScreen D LifecycleScreen: onPauseOrDispose

LifecycleScreen D LifecycleScreen: onStopOrDispose

LifecycleScreen D LifecycleScreen: onPauseOrDispose

LifecycleScreen D LifecycleScreen: onStopOrDispose

As you can observe in the above log , when back button is pressed , the onPauseOrDispose and onStopOrDispose are executed twice(i think its a bug) in case of LifecycleStartEffect and LifecycleResumeEffect but this is not the case when we use LifecycleEventEffect

Please run the code once in each code block to get better understanding of this lifecycle events and states in compose .

Please leave your comments to improve.

Happy and Enjoy coding