Skip to content

Instantly share code, notes, and snippets.

@GibsonRuitiari
Last active July 28, 2022 17:28
Show Gist options
  • Save GibsonRuitiari/7cb947228661993ee36d5c05b9e8f23f to your computer and use it in GitHub Desktop.
Save GibsonRuitiari/7cb947228661993ee36d5c05b9e8f23f to your computer and use it in GitHub Desktop.

Provides the reason as to why LocalContext.current as Activity is not suitable a suitable approach to get an Activity instance in Jetpack compose projects

The android system's view pipeline consists of two stages - measure and layout processes whereby the android framework measures the sizes of each view present in a view hierarchy (viewgroup) and proceeds to position them in specific positions on the screen. What this means is that the android framework only knows how to draw views on the android canvas.

Compose uses a similar logic to draw ui on its[compose] canvas. First, as you already know, Composes uses layout-nodes instead of views and for these nodes to be correctly positioned and drawn on the screen, it uses something called an applier(UiApplier).

As the name suggests the UiApplier transverses through the layout-nodes measuring and laying out (drawing) the nodes in specific positions on the Compose canvas. Think of compose system as a tree and the layout nodes as leaves.

Whatever is drawn on the Compose canvas needs to translated/implemented on the android canvas, there needs to be something that bridges compose and the android view system i.e the android framework needs to know how to draw these layout nodes on the screen and transform them into something the user can see.

So compose internally uses a AndroidComposeView [(AndroidComposeView file)] https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/ui/ui/src/androidMain/kotlin/androidx/compose/ui/platform/AndroidComposeView.android.kt;l=164?q=AndroidComposeView&sq=&ss=androidx%2Fplatform%2Fframeworks%2Fsupport) The AndroidComposeView extends a view group meaning that all the layout nodes used by compose are placed inside a view group, and the android framework draws this view group on the android canvas, using the ViewGroup's dispatchDraw() method.

To make sure the compose system and the android view system synchronize, the AndroidComposeView provides android system-specific properties such as context, lifecycle-owner, configuration etc. These properties are made available to the whole AndroidCompose system as composition locals using the ProvideAndroidCompositionLocals function. AndroidCompositionLocals

If you look at the method (ProvideAndroidCompositionLocals) you'll see that LocalContext.current is gotten from view.context, bearing in mind the view here is AndroidComposeView which is just a view-group, what this means then is that view.context is not necessarily an Activity, for all we know it can be a ContextWrapper .

Thus it might (note: might) be dangerous since we maybe expecting an Activity whereas what we have is a ContextWrapper ; hence be a potential source of app-crashes in the future.

Hence why I suggest the following approach when you want to get an instance of Activity in jetpack compose instead of casting LocalContext.current to Activity.

private fun Context.findActivity(): Activity {
    var context = this
    while (context is ContextWrapper) {
        if (context is Activity) return context
        context = context.baseContext
    }
    throw IllegalStateException("Activity absent")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment