Reputation: 815
I'm current implementing Compose Navigation in my app, and I've noticed that any screen with a TextField causes considerable delay between interaction and transition start. Are there any way to optimize the TextField performance?
I made a small StopWatch that starts when I tap the button in Screen1 and ends when Screen2 is composed.
Here's my NavHost
NavHost(
navController = navController,
startDestination = NavRoute.Screen1,
) {
composable<NavRoute.Screen1> {
Button(onClick = {
stopWatch.start()
navController.navigate(NavRoute.QuestionnaireScreen)
}) { Text(text = "Navigate") }
}
composable<NavRoute.Screen2> {
Screen2()
}
}
and here's my Screen2 layout
@Composable
fun Screen2() {
Text(text = "")
stopWatch.end()
}
Performance:
18:16:24.039 I tick=43 // Acceptable, practically unnoticable
18:16:25.600 I tick=37
18:16:26.574 I tick=36
18:16:27.441 I tick=34
But if I replace the Text with TextField
@Composable
fun Screen2() {
TextField(value = "", onValueChange = {})
stopWatch.end()
}
The performance shoots up for the first composition
18:21:43.679 I qqq tick=99 // OVER DOUBLE the delay, somewhat noticable
18:21:45.443 I qqq tick=49
18:21:47.286 I qqq tick=49
18:21:48.836 I qqq tick=48
Of course, 100ms is not that bad in isolation, but I'm currently working on an onboarding screen that has multiple TextFields, and multiple screens with multpl TextFields, and the delay becomes VERY noticable.
Example:
@Composable
fun Screen2() {
TextField(value = "", onValueChange = {})
TextField(value = "", onValueChange = {})
TextField(value = "", onValueChange = {})
TextField(value = "", onValueChange = {})
TextField(value = "", onValueChange = {})
TextField(value = "", onValueChange = {})
stopWatch.end()
}
Perfomance:
18:25:12.158 I tick=172 // Unacceptable performance, very noticable
18:25:14.710 I tick=114
18:25:16.683 I tick=105
18:25:17.864 I tick=94
18:25:18.967 I tick=94
18:25:20.055 I tick=78
StopWatch impl for reference
class StopWatch {
private var start: Long = 0
fun start() {
start = System.currentTimeMillis()
}
fun end() {
val end = System.currentTimeMillis()
println("tick=${end - start}")
}
}
val stopWatch = StopWatch()
Any help would be greatly appreciated!
Upvotes: 1
Views: 57
Reputation: 10887
You should not worry too much about performance while running the app in the debug variant. As soon as you build your app in release variant, and minification occurs, performance will improve a lot in Jetpack Compose.
I ran some basic benchmarking on your exact code. Note how the release variant with six TextField
s still is faster than the debug variant with a single Text
Composable:
Mode | Experiment | Benchmark |
---|---|---|
Debug | Text |
64ms |
Debug | 1 Textfield |
218ms |
Debug | 6 Textfield s |
301ms |
Release | Text |
11ms |
Release | 1 Textfield |
40ms |
Release | 6 Textfield s |
57ms |
If you want to test the release variant in your emulator, you can switch the build variant in Android Studio:
Please make sure that you have enabled minification in your release variant configuration. Additionally, when building a release variant, you need to provide a key to sign the build. For development, you can use the debug key in your build.gradle
file like this:
android {
compileSdk 34
//...
buildTypes {
release {
minifyEnabled true // enable minification
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' // default minification rules
signingConfig signingConfigs.debug // use debug key for signing
}
}
//...
}
You can use baseline profiles to further boost the performance of the app, especially during the app startup.
Upvotes: 1