Jetpack Compose — Optimize list performance with Key
Showing the list of items is quite a normal use case for many applications. However, the items can update the content on UI dynamically either by updating its content or its position in a list. Whenever item position is changed for a mutable list inside the LazyColumn, LazyRow, or inside the loop then it requires an optimized approach otherwise it tends to lose the existing composable and create newer ones.
So before we see how we can add optimization let's see about composable and the call site.
Every function which is annotated with @Composable will possess a piece of extra information to the Kotlin compiler that it contains a state that has to be transformed to UI. Now when the Kotlin compiler runs through the function it adds up the information about the call site, every call site has to be unique which is taken care by the compiler so no need to worry much about that.
What is a call site?
The place of calling a composable function is called the call site, which as stated before has to be unique as based on the call site the composable could be reused during recomposition. Let’s see an example
Here we have a multiple composable where the information regarding the user is displayed through two Text() composable. Here for Text(name) we have a unique call site and for Text(address) we have another call site as both are called from different places.
Now let's add a bit of a twist, how about placing the composable inside a loop?
Earlier we have seen in the sample that two Text composable was called one below the other so they both had a different call site but now when it's placed inside a for loop then the calling place will remain the same which means it’s the same call site but that violate the rule that every composable should have a unique call site.
Call site inside a loop
When composable is called from a loop then by default the Kotlin compiler will add the positional information along with the composable so as to make the call site distint. This helps to reuse composable during recomposition so that it will avoid creating a new composable, it certainly adds up to the performance benefit.
However, we have a concern. How about the list is mutable and the content could be added or removed in between the list, for example, let’s imagine we have a mutable list instead of immutable as shown above for the numbers between 0 to 100. Now a new item is added at the beginning of the list, what happens now?
As the composable call site was dependent on the item position but the item position in the list is completely changed as we have inserted a new item at the beginning of the list so now none of the composable could be reused even though the remaining content remains the same. That’s the major hit on the performance which has to be tackled but the question is HOW?
Add optimization through KEY
Now instead of depending on the compiler to make the call site unique let’s provide that extra information from our side, we can do that through a key.
Now we are explicitly providing a unique key for the call site using a key function so it will no longer depend on the item position inside the loop rather it will depend on the value provided as a parameter for the key, for us, it’s the emp.id
Let’s go back to the same question once again, how about the list is mutable and a new employee record is added at the beginning of the list. Well, in this case even though the item position got changed as a new item is inserted at the beginning still only for that new item a new composable will get created and for the rest of the items the old composable will get reused, hence we have now improved the performance just by providing the key parameter.
How about LazyColumn or LazyRow?
Well this sort of optimization is added to the list too it’s just a matter of fact that instead of calling a key function now pass the key through the lambda just like below
So whenever the list item is added or removed still it will not create the composable for all the items rather it will reuse the existing one as soon as the associated key is found.
We also get an extra advantage with this approach that is, when the list item is updated based on item position change then it will update the list along with animation. The animation support is added to compose-ui 1.1.0 which is currently in beta as of now.