Exploring the next generation design for Android apps with Jetpack Compose

tatuas (Tatsuya Sawai)
Eureka Engineering
Published in
4 min readDec 5, 2022

Since Jetpack Compose became stable, Android’s UI implementation has certainly evolved. At the same time, application design methods optimized for Android’s proprietary widget implementation have become a thing of the past, and Android app developers must now search for new optimal solutions. This situation will stimulate our own motivation to the utmost, as it enforces this blessed environment where there is still no clear-cut solution.

Here, we will examine what method is considered best for us through the Jetpack Compose concept.

Understanding Jetpack Compose

I will leave the details of Jetpack Compose to the official documentation, but everyone agrees that this library, which enables declarative UI implementations, has changed the paradigm of UI implementations in Android.

https://developer.android.com/jetpack/compose

Jetpack Compose has often been compared to the React’s worldview in the software community. However, for those of us who have long been Android experts in our app projects, this may not be the best explanation.

Android has two nice classes, RecyclerView and ListAdapter. ListAdapter requires a lot of Android-specific implementation, which can be daunting for beginning Android engineers. which can seem intimidating to a beginning Android engineer, but it was based on a kind of declarative UI philosophy. In contrast, Jetpack Compose is much cleaner, distancing itself from Android’s own dialect and allowing for high quality UI to be expressed through a pure and universally understandable implementation in the Kotlin language.

If you understand that Jetpack Compose is a kind of ListAdapter of sorts, imagining what the next generation of Android app implementations will look like will accelerate the possibilities for further Android development.

Design guidelines we should refer to

Android already has a great architecture proposed by Google, and it is obvious that the best way for an Android app engineer to understand it is to read the official documentation, rather than deciphering texts written by various Android app engineers. The most obvious way to understand is to read the official documentation, rather than trying to decipher texts written by different engineers.

https://developer.android.com/topic/architecture#recommended-app-arch

Although there are many architectures out there, there are certain common views regarding the underlying philosophy. There are three layers: the UI Layer, the Domain Layer, and the Infra Layer, and the data direction between each layer is probably one-way.

Fig.1 General Application Layer Concept

Now, to consider the relationship between these app layers and Jetpack Compose, we will break down the elements in each Layer.

Dive into the Layer design

From here, we will consider the role of each layer. Since it is obvious that the Infra layer exists as a source of data such as databases and APIs dependent on Android, we will refer to the other layers, i.e., UI and Domain.

UI Layer

The UI Layer, as the name implies, is the layer that displays the data. The UI must also change when changes are made to the data. From a design perspective, the UI layer would consist of UI Elements for displaying data and State Holders for temporarily caching data and handling logic.

Android app engineers have a reassuring ViewModel to help them navigate the complex lifecycle.

The ViewModel, with its simplified lifecycle, is a good companion to Compose and will ensure a stable and maintainable implementation by generating, maintaining, and notifying UI Elements of the UI model. The biggest difference between using Jetpack Compose to configure UI Elements and the previous Android Widget implementation is that you do not need to be aware of the Android-specific mechanisms until you get to the Compose implementation point. This is a strong advantage. Here is an example of sample code.

// Pure Kotlin ui data class!
data class MainUiState(val message: String)

// State Holder
class MainViewModel: ViewModel(private val useCase: MainUseCase) {
val uiState = MutableStateFlow(MainUiState(""))
init {
viewModelScope.launch {
useCase.loadMessage()
.onSuccess { result ->
uiState.update { it.copy(result.message) }
}
.onFailure {
uiState.update { it.copy("Error") }
}
{
}

// UI Elements
@Composable
Fun MainScreen(
Modifier: Modifier = Modifier.fillMaxSize(),
uiState: MainUiState,
) {
Text(
Text = stringResource(R.string.template, formatArgs = uiState.message)
)
}

// Presentation
class MainActivity: Activity() {
private val viewModel by viewModels()
override fun onCreate() {
setContent {
MainAppTheme {
val uiState by viewModel.uiState.collectAsState()
MainScreen(uiState)
}
}
}

By continually supplying models from ViewModel, Jetpack Compose, which is implemented to assemble UI in response to those models, faithfully renders UI from the supplied models. In addition, the opportunity to be aware of the management of resources, which is a class unique to Android, and the maintenance of View state by Bundle will be dramatically reduced.

Domain Layer

Domain Layer is Optional according to Google’s documentation. Nevertheless, the Domain Layer should be provided whenever possible. This is because the Domain Layer reconfigures the data passed from the Infra Layer according to the business specifications, and the State Holder of the UI Layer becomes much simpler. Ideally, the implementation of the Domain Layer should also be neutral and not completely dependent on the Android framework.

Conclusion

Many of the ideas introduced here are scattered in the field of apps that, in principle, require new implementation using Jetpack Compose. If you are able to sublimate these ideas to the point where they become as common as if they were air, without being aware of the technology that makes the application possible, this is a victory for Android app engineers, and we could not be happier.

--

--