HomeAndroidEasy methods to Debug Jetpack Compose Recomposition with Logging?

Easy methods to Debug Jetpack Compose Recomposition with Logging?


Recomposition in Jetpack Compose is a posh matter. It’s complicated as a result of typically you haven’t any concept why a sure perform is recomposed, which isn’t what you anticipated primarily based in your data. Thus, you’ll want to debug it.

Breakpoints in Debugger

Utilizing breakpoints in a debugger first involves my thoughts to debug recomposition. Nonetheless, there are a number of limitations to this strategy.

Commonplace Logging

So to debug with logging, you employ Log.d. It seems like this

Log.d("DebugRecomposition", "RecompositionExample() perform scope")

Nevertheless it missed one essential piece of knowledge, which does not inform the present recompose scope info. This info is essential as a result of completely different composable capabilities can nonetheless have the identical recompose scope – see the reason beneath.

To print this recompose scope info, you employ $currentRecomposeScope. Now the logging seems like this

Log.d("DebugRecomposition", "RecompositionExample() perform scope $currentRecomposeScope")

The log output seems like this:

D/DebugRecomposition: RecompositionExample() perform androidx.compose.runtime.RecomposeScopeImpl@894fab8

This RecomposeScopeImpl@894fab8 is the distinctive ID for this recompose scope. If one other composable perform has the identical distinctive ID, it means it additionally belongs to this identical recompose scope.

Properly, there’s nonetheless one lacking piece of knowledge – the recomposition depend. Technically, you continue to can manually depend the log assertion, however that may be very troublesome and vulnerable to error. Due to that, you want customized logging.

Customized Logging

I steal the customized logging code from this superb submit about recomposition right here, and I make a number of modifications to it as a result of I feel some stuff is simply pointless. Right here is the modified model.

class RecompositionCounter(var worth: Int)

@Composable
inline enjoyable LogCompositions(tag: String, msg: String) {
    if (BuildConfig.DEBUG) {
        val recompositionCounter = keep in mind { RecompositionCounter(0) }

        Log.d(tag, "$msg ${recompositionCounter.worth} $currentRecomposeScope")
        recompositionCounter.worth++
    }
}
  • I renamed class Ref to class RecompositionCounterto higher mirror it’s the recomposition depend

  • I eliminated SideEffect {} and moved the counter increment after the logging. I don’t assume we want SideEffect {}right here.

  • I added $currentRecomposeScope as further info which I feel is essential.

The inline is to make sure the mum or dad who calls this composable perform has the identical composable perform scope. So as phrases, when a mum or dad is recomposed, this LogCompositions() perform undoubtedly shall be referred to as.

Examples

Let us take a look at a easy instance beneath.

@Composable
enjoyable RecompositionExample() {
    var depend by keep in mind { mutableStateOf(0) }

    LogCompositions("DebugRecomposition", "RecompositionExample() perform scope")

    Column {

        LogCompositions("DebugRecomposition", "Column() content material scope")

        MyButton(onClick = { depend++ }, textual content = depend.toString())

    }
}

@Composable
enjoyable MyButton(
    onClick: () -> Unit,
    textual content: String) {

    LogCompositions("DebugRecomposition", "MyButton() perform")

    Button(onClick = onClick) {

        LogCompositions("DebugRecomposition", "Button() content material")

        Textual content(
            textual content = textual content,
        )
    }
}

The log output seems like this throughout start-up:

D/DebugRecomposition: RecompositionExample() perform scope 0 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: Column() content material scope 0 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: MyButton() perform 0 androidx.compose.runtime.RecomposeScopeImpl@399bf6

D/DebugRecomposition: Button() content material 0 androidx.compose.runtime.RecomposeScopeImpl@dc1e8e2
  • You discover the RecompositionExample() and Column() have the identical recompose scope. It’s because frequent layouts equivalent to Column(), Row(), and Field() are all “inline” composable capabilities. Thus, they’ve the SAME recompose scope as their callers.

Should you click on the button, the log output seems like this:

D/DebugRecomposition: RecompositionExample() perform scope 1 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: Column() content material scope 1 androidx.compose.runtime.RecomposeScopeImpl@894fab8

D/DebugRecomposition: MyButton() perform 1 androidx.compose.runtime.RecomposeScopeImpl@399bf6

D/DebugRecomposition: Button() content material 1 androidx.compose.runtime.RecomposeScopeImpl@dc1e8e2
  • When the button is clicked, the depend state is mutated. Thus, all to recompose scopes that learn the state shall be recomposed.

  • In column() scope, it reads the depend state from textual content = depend.toString(). Thus, column() is recomposed. As a result of column() and RecompositionExample() has the identical recompose scope, RecompositionExample() is recomposed as nicely.

  • MyButton() is recomposed as a result of the enter parameter textual content is modified. Scope that learn the textual content shall be recomposed. Thus, Button() and Textual content() are recomposed too. There isn’t a logging for Textual content(), so it would not present up within the log.

Conclusion

As talked about, recompose is a posh matter. This text would not give attention to why and the way recomposition can occur. It covers a bit within the examples above, however it’s only a pretty primary demonstration.

This text exhibits how one can debug it utilizing the customized logging LogCompositions() to determine how recomposition behaves. In my view, recomposition is crucial idea to grasp in Jetpack Compose, understanding the way it works is essential.

Supply Code

GitHub Repository: Demo_UnderstandComposeConcept

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments