Blog Infos
Author
Published
Topics
, , , ,
Published
Jetpack Compose Previews

 

Jetpack Compose Previews are a great tool for rapidly iterating over UI design. Understanding their underlying mechanics is optional for everyday tasks but invaluable when facing uncommon problems.

In this article, we’ll explore how Jetpack Compose Previews work. We’ll also cover how to run them using adb, the nuances in a multi-modular project, things we can customize, and even how we can preview a non @Preview annotated composable.

At the end of this article, you’ll find a complete sample project and a few practical use cases where this knowledge proves useful.

Composable preview modes

We’ll categorize preview inspection into two modes:

  • Design inspection through the Android Studio design window.
  • Runtime inspection by running previews into an emulator or device.

Note: This article focuses exclusively on runtime inspection mode.

How do runtime previews work?

The core component and heart of runtime previews is the androidx.compose.ui.tooling.PreviewActivity. This activity is responsible for hosting the composable annotated with the @Preview annotation, passing its @PreviewParameter data, and setting up the preview appearance according to the properties specified in the @Preview annotation.

PreviewActivity lives in the androidx.compose.ui:ui-tooling artifact, which we usually add as the Gradle dependencies:

implementation("androidx.compose.ui:ui-tooling-preview")
debugImplementation("androidx.compose.ui:ui-tooling")

These contain the preview annotations and the required classes that support the Jetpack Compose Preview tooling.

Runtime previews are instrumented APKs

Whenever we run a preview through the run button on the left, it triggers a compilation for the androidTest source set of the module where the preview is defined:

:module — run preview

 

You can verify this by checking the executed Gradle tasks in the build output tab of Android Studio:

The output of the :assembleDebugAndroidTest task is an APK generated for instrumentation. Anecdotally, this APK is used to run the preview; however, there is an exception when the preview is run through the :app module, in which case the APK won’t be used. Since this exception does not significantly impact the overall process, we will assume that the APK is used consistently.

Lastly, Android Studio will install the instrumented APK and launch the PreviewActivity, passing information about which composable preview to render.

Launching ModuleComposablePreview on 'Pixel 7 API 34'.
Starting: Intent { 
  act=android.intent.action.MAIN 
  cat=[android.intent.category.LAUNCHER] 
  cmp=me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity (has extras) 
}

Open logcat panel for emulator Pixel 7 API 34
Connected to process 7329 on device 'Pixel_7_API_34 [emulator-5554]'.
Inspecting the AndroidManifest.xml of the preview

Running the ModuleComposablePreview results in the generation of the APK module/build/intermediates/apk/androidTest/debug/module-debug-androidTest.apk.

The ModuleComposablePreview is located in the :module of the sample project.

By inspecting the AndroidManifest.xml of this APK, we can uncover the following details:

  • The instrumentation tag is present. Even though previews are compiled into an instrumented APK, they run as normal applications, meaning that any instrumentation customization won’t be used:
<instrumentation
   android:label="Tests for me.jansv.previewinspect.module.test"
   android:name="androidx.test.runner.AndroidJUnitRunner"
   android:targetPackage="me.jansv.previewinspect.module.test"
   android:handleProfiling="false"
   android:functionalTest="false" />
  • By placing an AndroidManifest.xml under the module/src/androidTest source set, you can customize certain aspects of the preview, such as the Application instance or the app’s label:
<application
  android:label="Module Label"
  android:name="me.jansv.previewinspect.module.ModuleInstrumentedApplication"
  android:debuggable="true"
  android:extractNativeLibs="false">
  ...
</application>
  • The APK does not include an activity with the android.intent.action.MAIN intent action, meaning it won’t appear on the device’s home screen.

These insightful hints suggest we can perform a preview run entirely manually, so let’s find out how!

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

, ,

Kobweb:Creating websites in Kotlin leveraging Compose HTML

Kobweb is a Kotlin web framework that aims to make web development enjoyable by building on top of Compose HTML and drawing inspiration from Jetpack Compose.
Watch Video

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kobweb

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author o ...

Kobweb:Creating websites in Kotlin leveraging Compose HTML

David Herman
Ex-Googler, author of Kob ...

Jobs

Running previews without Android Studio

Using the adb command-line tool, you can run a composable preview without Android Studio.

The following steps use the sample project as a playground

Step 1: Generate the preview instrumented APK
./gradlew :module:assembleDebugAndroidTest

Once complete, the task will generate the APK at: module/build/outputs/apk/androidTest/debug/module-debug-androidTest.apk, which we will install as the next step.

Note: This APK is located in a different path than the one generated by Android Studio.

Step 2: Install the APK in a device or emulator.
adb install -r -t \
  module/build/outputs/apk/androidTest/debug/module-debug-androidTest.apk

Since this APK does not have an activity with the android.intent.action.MAIN intent action, it will not appear on the device’s home screen.

Step 3: Launch the preview

Until now, we haven’t discussed how PreviewActivity determines which Jetpack Compose preview to render. The activity requires two intent extra parameters to specify the composable to preview:

  • composable: A fully qualified path to the @Preview composable function to render.
  • parameterProviderClassName: A fully qualified class name of the PreviewParameterProvider subclass used to populate the preview parameter annotated with @PreviewParameter.

PreviewActivity uses these intent extras to bring these entities to life through reflection.

The preview we want to launch is defined as follows:

// ModuleComposable.kt

package me.jansv.previewinspect.module

@Composable
fun ModuleComposable(text: String? = null) { ... }

@Preview
@Composable
fun ModuleComposablePreview() = ModuleComposable()

@Preview
@Composable
fun ModuleComposablePreview(
  @PreviewParameter(TextParameterProvider::class) text: String,   
) = ModuleComposable(text = text)

class TextParameterProvider : PreviewParameterProvider<String> {
    override val values = (1..5).map { "Param #$it" }.asSequence()
}

The fully qualified path for the ModuleComposablePreview is: me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview.

With this, we can finally launch the preview:

adb shell am start \
 -n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \
 -e composable "me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview"
Jetpack Compose @Preview started with adb

With this command, we have started the PreviewActivity as a component of the installed APK. The package name of this APK is me.jansv.previewinspect.module.test which combines the module’s namespace with the .test suffix typically added to instrumented APKs.

This is good 🎉 but let’s push it a bit more…

Additionally, you can run the ModuleComposablePreview with a parameter provider class passing in the parameterProviderClassName:

adb shell am start \
 -n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \
 -e composable "me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview" \
 -e parameterProviderClassName "me.jansv.previewinspect.module.TextParameterProvider"

And yet a bit more…

How about non @Preview composables?

It turns out you can leverage this method to preview a composable that is not annotated with @Preview. For example, you can use the same command to preview ModuleComposable.

adb shell am start \
 -n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \
 -e composable "me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposable"
Previews in a multi-module setup

Some considerations to keep in mind regarding the differences between running previews in the :app module versus a separate module are:

  • The instrumented APK is relevant only for modules that don’t produce an APK, such as those applying the Android library plugin. Since this is not the case for the :app module, the default APK is used instead.
  • Because the instrumented APK generated for the :app module is not used for previews, no customizations made in the app/src/androidTest source set will be applied.
  • Previews for library modules can be customized through <module>/src/androidTest. For example, you can modify the Application instance, the PreviewActivity theme, the label, and more.
  • If the module you run the preview from introduces AndroidManifest.xml placeholders that are only provided through the :app module, the module will need to supply them for the instrumented APK as well for the compilation to work. You can leverage Gradle to supply them conditionally:
// module's build.gradle.kts

androidComponents {
    onVariants(selector().withBuildType("debug")) {
        it.androidTest?.manifestPlaceholders?.put("placeholder", "value")
    }
}
Conclusion

Jetpack Compose Previews are powerful for inspecting and refining your composables. This deep dive covered running previews with adb, understanding their compilation, and previewing composables without the @Preview annotation.

Insights into handling multi-module setups and customizations provide a comprehensive understanding to maximize their utility. With this knowledge, you can streamline your development process and tackle complex preview scenarios confidently.

Thanks for reading and I hope you found this guide useful 🙌🏼

Sample app

https://github.com/janselv/JetpackPreviewInspectSample?source=post_page—–57601ea32ebc——————————–

This article is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
In this part of our series on introducing Jetpack Compose into an existing project,…
READ MORE
blog
In the world of Jetpack Compose, where designing reusable and customizable UI components is…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
Menu