Jetpack Compose оптимизация производительности списка

от автора

image

Я в своем время наткнулся на довольно интересное поведение composable функций в списках, тогда мне помогла статья с медиума и чтение книги по compose internals, ссылки на них я приложу в конце статьи. Решил поделиться, возможно кому-то это поможет)

Отображение списков распространенный кейс для большинства приложений и зачастую эти списки динамические, в них меняется контент, позиции элементов и т.д. Есть довольно быстрый способ оптимизировать ваш список в compose.
Но для начала рассмотрим, что такое «место вызова» (call site), composable функции.

Composable

Каждая функция, помеченная @Composable аннотацией будет иметь дополнительную информацию для kotlin компилятора о том, что она эмитит UI. Когда компилятор проходит по этой функции он добавляет информацию о месте ее вызова, эти данные являются уникальным идентификатором для composable функции.

Что такое место вызова (call site)

Информация о месте вызова необходима для переиспользования во время стадии recomposition дерева composable функций.

@Composable fun Example(     firstText: String,     secondText: String ) {     Column() {         Text(firstText) // место вызова для composable с firstText         Text(secondText) // место вызова для composable с secondText     } } 

Так как обе composable функции вызываются в разных местах, то у каждой их них есть свой уникальный идентификатор места вызова.
Но давайте посмотрим на такой пример:

@Composable fun Loop() {     Column {         for (num in 1..0) {             Text(num.toString())         }     } } 

Можно заметить, что теперь место вызова всегда одинаковое, это нарушает правило уникального идентификатора. В таких кейсах по дефолту компилятор котлина добавляет к информации о месте вызова еще и позицию в списке. Это позволяет переиспользовать composable функции во время процесса recomposition.

Но у нас проблемы по коням Андрюха, у нас криминал.
Давайте представим, что список модифицируемый. Что произойдет если новый элемент добавится в начало списка? Место вызова каждой созданной composable функции зависело от позиции элемента в списке, так как все позиции поменялись теперь ни одно произведенное вычисление composable функций не может быть переиспользовано, хотя контент не менялся. Такое поведение стоит поправить.

Оптимизация через Key

Давайте предоставлять уникальную информацию для места вызова вместо компилятора

@Composable fun Loop() {     Column {         for (someObj in ourList) {             key(someObj.key) {                 Text(someObj)             }         }     } } 

Теперь место вызова еще имеет информацию которую мы предоставляем через key. В таком кейсе при добавлении нового элемента в начало списка новая composable функция инициализируется только для этого айтема, все остальные заэмиченные composable функции будут переиспользованы.
Такую оптимизацию мы также можем добавить для LazyColumn и LazyRow:

@Composable fun Loop() {     LazyColumn(ourList, {someObject -> someObject.id}){         Text(someObj)     } } 

Список вдоховителей:

Статья освещающая данную тему на английском — pankaj-rai.medium.com/jetpack-compose-optimize-list-performance-with-key-1066567339f9«
Полезная книга по внутренностям compose — jorgecastillo.dev/book


ссылка на оригинал статьи https://habr.com/ru/post/645799/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *