jetc.dev Newsletter Issue #91

Published: 2021-11-16

This week, we look at the impacts of orientation changes on BoxWithConstraints(), see what Jake Wharton has been up to, and create custom themes (with or without MaterialTheme()). We render some treemaps and look at using blurs as image placeholders. Plus, I wonder how tooling is going to handle composition-related memory leaks.

Also… we look at ellipsizing text.

One Off the Stack, One Off the Slack

You’ve got questions. That’s understandable!

How Do We Ellipsize Based on Height?

Text() supports an overflow parameter, and a popular option for that is TextOverflow.Ellipsis. However, this only works with maxLines; if you do not specify maxLines, you do not get an ellipsis, even if the text is truncated. While we wait for formal support for this, see how to implement a workaround in this week’s highlighted Stack Overflow question!

When Do Orientation Changes Take Effect?

Orientations are confusing enough in ordinary Android code. While Compose UI handles configuration changes for us, we still need to react to the new orientation. Sometimes that is easy, and sometimes… not so much, as we see in this week’s Kotlinlang #compose Slack thread.

Composable Commentary

Posts, videos, and other new information related to Jetpack Compose!

Videos: droidcon Berlin 2021

droidcon Berlin had a bunch of sessions on Compose UI, and their videos are now up, including:

The State of Managing State (with Compose)

Jake Wharton returns, this time with a brief look at Molecule, a Cash App library that uses composables to drive the emissions of a StateFlow or Flow, instead of (or perhaps in addition to) having composables that render UI.

Video: Optimizing Render Performance of Jetpack Compose

William Shelor delivered an Android Worldwide presentation looking at why our Compose UI screens may be sluggish. Some of that is environmental (e.g., R8, JIT), and some is tied to how we write our composables (e.g., using keys properly).

Medium: Fairly Evaluating the Impact of Different Android UI Libraries on Gradle Build

Another type of performance is build performance. Chao Zhang wanted to build atop of Chris Banes’ build analysis of Tivi and to compare and contrast different UI construction approaches. TL;DR: data binding is really slow.

Medium: Application Theme in Jetpack Compose

Maryam Alhuthayfi explores creating custom versions of MaterialTheme(), complete with custom colors, fonts, and shapes.

Medium: How to Create a Truly Custom Theme in Jetpack Compose

Dmytro Shuba walks us through the same basic process: how to create a custom theme. In this case, Dmytro bypasses MaterialTheme() and looks at how you can create a theme tied to your own app’s design system, for use with non-Material composables.

Jetpack Compose: Tabs with Swiping

Often, in Android, tabs allow for swipe gestures to switch between tab contents, instead of tapping on the tabs themselves. This is not offered “out of the box” by Compose Material. Bevan Steele explores using Accompanist to fill in this feature gap.

Integrating ZXing Android Embedded in a Compose App

Thomas Künneth is back, taking another look at something from outside of Compose UI and determining how best to use it in a composable. Pretty much everything tied to SurfaceView and TextureView will need this sort of attention, and this time Thomas explores ZXing’s embedded QR code scanner. In this case, Thomas inflates a layout containing a DecoratedBarcodeView, then manages the resulting view in a composable hierarchy.

Using SwiftUI and Compose to develop App Widgets on iOS and Android

With iOS 14, Apple started offering app widgets, which Android had for a decade prior to this. However, Google largely ignored app widgets for several years, and the original Android 1.6-era API for implementing app widgets left a lot to be desired. John O’Reilly wanted to use Kotlin/Multiplatform to drive app widgets for both iOS and Android. In this post, John explores how this went, with a quick peek at the Glance API for defining Android app widgets using composables.

Resource Roundup

100% pure code!

GitHub: microsoft / surface-duo-compose-sdk

Microsoft is starting on a library for composables of particular relevance to foldables, such as the Microsoft Surface Duo family. The initial composable is TwoPaneLayout(), for organizing two panes’ worth of content both on foldables and on tablets.

GitHub: overpas / compose-treemap-chart

Pavel Shurmilov brings us a library that renders a treemap, a type of chart that uses captioned areas to represent relative values in some data set. Pavel’s implementation offers a TreemapChart() composable, plus a DSL for constructing a tree of nodes to use for the chart data.

GitHub: orlando-dev-code / compose-image-blurhash

Blurhash is a system for converting an image into a placeholder representation in 20-30 characters. The idea is that a Web service could ship the blurhash value along with an image URL in a response (e.g., JSON). The client app could render the placeholder immediately, then swap in the full image when it is ready. Orlando Novas Rodriguez implemented an ImageBlur() composable that handles rendering the blurhash placeholder, while delegating the real image loading to Coil.

Gist: EmmanuelGuther / ComposeGradientModifier.kt

Emmanuel Gutierrez Hernandez created a small gradientBackground() modifier that implements a linear gradient at the specified angle using a supplied list of colors.

…And One More Thing

Last week, I wrote about one new-to-us problem in Compose: overcomposing, where we cause a composable to recompose a lot more frequently than we expect, burning CPU cycles and causing jank.

Another new-to-us problem was pointed out by Ryan Kay and Google’s Adam Powell in a “One Off the Slack” entry from a month ago: compositions hold references to objects. So long as the composition is outstanding, anything the composition references cannot be garbage-collected.

As Adam pointed out, these things get cleaned up eventually:

If you remember {} something it will be hard referenced by the composition. Any lambda captures you create that Compose might need to re-run to recompose will also be hard referenced by the composition. These references are dropped when the @Composable caller leaves the composition, or when the composition itself is disposed.

However, the underlying assumption there is that our compositions get disposed of when we think that they are. Since we do not really visualize compositions, only their results, it is possible that we would leak memory unexpectedly.

This gets exacerbated as we start using Compose for things outside of the still-new “stomping grounds” of Compose UI. Composables used purely for data management might get held onto longer than we expect. For example, Cash App’s Molecule is an interesting way to have a composable drive a StateFlow or Flow. Probably the composition is disposed when the associated CoroutineScope is cancelled. However, now you have to make sure that you are using the proper CoroutineScope, one that will get cleaned up at the appropriate time.

These sorts of leaks are likely to be difficult to analyze with your typical sorts of heap dumps, simply because too much of the intervening code is not ours and is not familiar to us. We are unlikely to recognize what a composition looks like, for example, let alone one set up by Molecule and referenced by some CoroutineScope. Perhaps Leak Canary will be able to help here in time.

These sorts of problems — overcomposing, composition-based memory leaks, etc. — highlight the “two steps forward, one step back” nature of early-term Compose UI work. Compose UI significantly simplifies many aspects of Android app development, but it does create new variations on problems. Until we get the tools or techniques to help solve those problems, be prepared for “bumps in the road” as we try scaling Compose UI up for larger and more complex apps.