DEV Community

ViO Tech
ViO Tech

Posted on

🚀 Jetpack Compose Senior Handbook 13 Kỹ Thuật Jetpack Compose Mà Android Senior Developer Sử Dụng Hàng Ngày

Chương 1: derivedStateOf – Vũ Khí Chống Recomposition Thừa Trong Jetpack Compose

Giới thiệu

Một trong những điểm khác biệt lớn nhất giữa Jetpack Compose và View System truyền thống là cơ chế Recomposition.

Compose rất thông minh trong việc chỉ cập nhật những phần UI cần thiết. Tuy nhiên, nếu không quản lý state đúng cách, ứng dụng vẫn có thể xảy ra:

  • Recomposition quá nhiều
  • Tăng CPU usage
  • Giảm FPS khi scroll
  • Lag trên các màn hình phức tạp

Đó là lý do derivedStateOf ra đời.

API này cho phép chúng ta tạo ra một state dẫn xuất (derived state), giúp Compose chỉ thực hiện recomposition khi giá trị cuối cùng thực sự thay đổi.


Vấn đề thường gặp

Giả sử chúng ta có một danh sách dài:

@Composable
fun UserList() {
    val listState = rememberLazyListState()

    val showButton =
        listState.firstVisibleItemIndex > 0

    Box {

        LazyColumn(
            state = listState
        ) {
            items(1000) {
                Text("Item $it")
            }
        }

        if (showButton) {
            ScrollToTopButton()
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Đoạn code trên hoạt động chính xác.

Tuy nhiên:

listState.firstVisibleItemIndex
Enter fullscreen mode Exit fullscreen mode

thay đổi liên tục trong quá trình scroll.

Ví dụ:

0
1
2
3
4
5
6
7
...
Enter fullscreen mode Exit fullscreen mode

Mỗi lần giá trị thay đổi, Compose sẽ đánh giá lại phần UI phụ thuộc vào state đó.

Trên các màn hình lớn, điều này có thể tạo ra lượng recomposition không cần thiết.


derivedStateOf là gì?

derivedStateOf cho phép tạo một state mới từ các state khác.

val showButton by remember {
    derivedStateOf {
        listState.firstVisibleItemIndex > 0
    }
}
Enter fullscreen mode Exit fullscreen mode

Compose sẽ theo dõi giá trị trả về.

Chỉ khi kết quả thực sự thay đổi:

false -> true
Enter fullscreen mode Exit fullscreen mode

hoặc

true -> false
Enter fullscreen mode Exit fullscreen mode

thì UI mới được thông báo cập nhật.


Điều gì xảy ra bên trong?

Không sử dụng derivedStateOf:

0
1
2
3
4
5
6
Enter fullscreen mode Exit fullscreen mode

Compose liên tục nhận state mới.


Sử dụng derivedStateOf:

false
true
true
true
true
true
Enter fullscreen mode Exit fullscreen mode

Compose chỉ quan tâm đến việc giá trị cuối cùng có thay đổi hay không.

Kết quả là số lần recomposition giảm đáng kể.


Ví dụ thực tế: Scroll To Top Button

@Composable
fun HomeScreen() {

    val listState =
        rememberLazyListState()

    val showScrollTop by remember {
        derivedStateOf {
            listState.firstVisibleItemIndex > 5
        }
    }

    Box {

        LazyColumn(
            state = listState
        ) {
            items(500) {
                Text("Item $it")
            }
        }

        AnimatedVisibility(
            visible = showScrollTop
        ) {

            FloatingActionButton(
                onClick = {}
            ) {
                Icon(
                    Icons.Default.KeyboardArrowUp,
                    contentDescription = null
                )
            }
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Lúc này nút chỉ xuất hiện khi người dùng scroll quá item thứ 5.


Ví dụ thực tế: Form Validation

Một màn hình đăng nhập đơn giản:

var email by remember {
    mutableStateOf("")
}

var password by remember {
    mutableStateOf("")
}

var termsAccepted by remember {
    mutableStateOf(false)
}
Enter fullscreen mode Exit fullscreen mode

Nút Login chỉ được bật khi:

email.isNotBlank() &&
password.length >= 8 &&
termsAccepted
Enter fullscreen mode Exit fullscreen mode

Sử dụng:

val isValid by remember(
    email,
    password,
    termsAccepted
) {
    derivedStateOf {
        email.isNotBlank() &&
        password.length >= 8 &&
        termsAccepted
    }
}
Enter fullscreen mode Exit fullscreen mode

Sau đó:

Button(
    enabled = isValid,
    onClick = {}
) {
    Text("Login")
}
Enter fullscreen mode Exit fullscreen mode

Code rõ ràng hơn và dễ bảo trì hơn.


Ví dụ thực tế: AdMob

Trong nhiều ứng dụng miễn phí:

val shouldShowAds by remember {
    derivedStateOf {
        !userState.isPremium
    }
}
Enter fullscreen mode Exit fullscreen mode

Hiển thị banner:

if (shouldShowAds) {
    BannerAd()
}
Enter fullscreen mode Exit fullscreen mode

Compose sẽ không phải đánh giá lại logic quảng cáo mỗi khi các state không liên quan thay đổi.


Những sai lầm phổ biến

Sai lầm #1: Dùng cho phép tính đơn giản

val fullName by derivedStateOf {
    "$firstName $lastName"
}
Enter fullscreen mode Exit fullscreen mode

Không cần thiết.

Chi phí tính toán thấp hơn nhiều so với chi phí quản lý derived state.


Sai lầm #2: Chỉ bọc lại state

val count by derivedStateOf {
    uiState.count
}
Enter fullscreen mode Exit fullscreen mode

Không mang lại lợi ích nào.


Sai lầm #3: Lạm dụng mọi nơi

derivedStateOf { ... }
derivedStateOf { ... }
derivedStateOf { ... }
derivedStateOf { ... }
Enter fullscreen mode Exit fullscreen mode

derivedStateOf không miễn phí.

Chỉ nên dùng khi thực sự cần tối ưu.


Khi nào nên sử dụng?

Scroll State

LazyListState
PagerState
ScrollState
Enter fullscreen mode Exit fullscreen mode

Animation State

Animatable
Transition
Enter fullscreen mode Exit fullscreen mode

Tính toán tốn kém

filter()
groupBy()
map()
sortedBy()
Enter fullscreen mode Exit fullscreen mode

Chỉ quan tâm đến kết quả cuối

Ví dụ:

isValid
showButton
shouldShowAds
Enter fullscreen mode Exit fullscreen mode

Khi nào không nên sử dụng?

Không nên dùng cho:

count + 1
Enter fullscreen mode Exit fullscreen mode
"$firstName $lastName"
Enter fullscreen mode Exit fullscreen mode
Text("Hello")
Enter fullscreen mode Exit fullscreen mode

Các phép tính cực kỳ nhẹ.


Quy tắc Senior Compose

Một nguyên tắc đơn giản:

Nếu state thay đổi hàng chục lần mỗi giây nhưng UI chỉ cần phản ứng khi kết quả cuối thay đổi, hãy cân nhắc sử dụng derivedStateOf.

Điều này đặc biệt hữu ích với:

  • LazyColumn
  • HorizontalPager
  • Animation
  • Gesture
  • Ad Visibility
  • Form Validation

Kết luận

derivedStateOf là một trong những API quan trọng nhất để tối ưu hiệu năng Compose.

Lợi ích chính:

  • Giảm số lần recomposition
  • Giảm CPU usage
  • Tăng FPS khi scroll
  • Tối ưu màn hình lớn
  • Giúp code rõ ràng hơn

Trong các dự án Compose production, đây là một kỹ thuật xuất hiện rất thường xuyên ở những màn hình có lượng state thay đổi liên tục.

Top comments (0)