remember là một hàm trong Jetpack Compose cho phép bạn giữ lại một giá trị giữa các lần tái kết hợp, ngăn chặn việc đặt lại trạng thái mỗi khi khởi động lại hàm Composable. Nếu không có remember, tất cả các biến được khai báo bên trong hàm sẽ bị đặt lại mỗi khi UI được cập nhật, khiến trạng thái trở nên không ổn định và vô dụng. Theo Google Android Developers, 2026, việc sử dụng remember đúng cách là nền tảng của quản lý trạng thái chính xác trong UI khai báo.
Điểm Chính
remember là một hàm tích hợp trong Jetpack Compose từ gói androidx.compose.runtime tạo ra một đối tượng giữ lại giá trị của nó giữa các lần gọi liên tiếp của một hàm Composable. Về mặt kỹ thuật, remember hoạt động với các slot — các ô nhớ đặc biệt được cấp phát cho mỗi vị trí gọi hàm trong cây UI. Miễn là hàm vẫn còn trong kết hợp (không bị xóa qua điều kiện if), remember sẽ trả về giá trị đã lưu ở mỗi lần tái kết hợp.
Cú pháp của remember rất đơn giản: bên trong dấu ngoặc nhọn, bạn chỉ định khối tính toán giá trị ban đầu. Khối chỉ được thực thi một lần — trong lần kết hợp đầu tiên của hàm. Tất cả các lần tái kết hợp tiếp theo trả về giá trị đã lưu mà không thực thi lại khối. Tuy nhiên, nếu một tham số khóa được chỉ định, khối sẽ được thực thi lại khi khóa thay đổi, cho phép tính toán lại giá trị dựa trên dữ liệu đầu vào.
remember không phải là phép thuật của Kotlin — nó là một hàm đơn giản với tham số khóa và lambda tính toán. Triển khai nội bộ của nó sử dụng Composer để đọc và ghi vào các slot. Trình biên dịch Kotlin, khi thấy một lệnh gọi remember, sẽ tạo ra mã truy cập vào CompositionContext hiện tại và quản lý các slot. Điều này có nghĩa là remember chỉ có thể được gọi bên trong hàm Composable hoặc một hàm khác trong ngữ cảnh kết hợp.
Trong Jetpack Compose, có ba biến thể chính của remember, mỗi loại có mục đích riêng. Remember cơ bản chỉ giữ lại giá trị trong bộ nhớ của tiến trình hiện tại — khi xoay màn hình (thay đổi cấu hình), Compose tạo lại Composition và tất cả các giá trị remember đều bị đặt lại. Để giữ lại dữ liệu trong quá trình thay đổi cấu hình và dừng tiến trình, người ta sử dụng rememberSaveable.
rememberSaveable tuần tự hóa giá trị vào Bundle thông qua cơ chế SavedStateHandle hoặc Parcelable. Điều này cho phép sống sót qua việc xoay màn hình, cài đặt "Không giữ hoạt động" và thậm chí cả việc thu nhỏ ứng dụng tạm thời. Tuy nhiên, rememberSaveable áp đặt các hạn chế về loại dữ liệu có thể lưu trữ: chúng phải là nguyên thủy, Parcelable, Serializable hoặc hỗ trợ bộ chuyển đổi Saver.
derivedStateOf không phải là cơ chế lưu giữ mà là một sự tối ưu hóa. Nó tạo ra một State có giá trị được tính toán từ các đối tượng State khác. derivedStateOf phản ứng với các thay đổi trong trạng thái nguồn nhưng chỉ tính toán lại giá trị khi có người đăng ký. Nếu lần tái kết hợp hiện tại không đọc derivedStateOf, việc tính toán sẽ không được thực hiện, tiết kiệm tài nguyên cho các cập nhật thường xuyên nhưng không cần thiết.
@Composable
fun RememberVariants() {
// 1. Remember cơ bản: tồn tại cho đến khi rời khỏi kết hợp
val createdAt = remember { System.currentTimeMillis() }
// 2. rememberSaveable: sống sót qua xoay màn hình
var username by rememberSaveable { mutableStateOf("") }
// 3. derivedStateOf: chỉ tính toán khi cần
val isButtonVisible = remember {
derivedStateOf { username.length() > 3 }
}
Text("Đã tạo: $createdAt")
TextField(value = username, onValueChange = { username = it })
if (isButtonVisible.value) {
Text("Nút sẽ được hiển thị")
}
}
Sự lựa chọn giữa remember và rememberSaveable phụ thuộc vào mức độ quan trọng của việc giữ lại dữ liệu trong quá trình thay đổi cấu hình. Đối với các trạng thái tạm thời không quan trọng nếu mất khi xoay (hoạt ảnh, vị trí cuộn hiện tại, trạng thái tiêu điểm) — remember thông thường là đủ. Đối với dữ liệu quan trọng (văn bản trong biểu mẫu, các mục đã chọn, hộp kiểm) — hãy sử dụng rememberSaveable.
Hiệu suất: rememberSaveable chậm hơn remember thông thường vì nó yêu cầu tuần tự hóa vào Bundle. Chỉ sử dụng rememberSaveable cho dữ liệu thực sự cần sống sót qua việc tạo lại Activity. Đối với mọi thứ khác — remember đơn giản. Việc sử dụng quá mức rememberSaveable dẫn đến chậm khi xoay màn hình và chuyển đổi giữa các ứng dụng.
Nếu bạn làm việc với các lớp không hỗ trợ Parcelable hoặc Serializable, hãy sử dụng Saver — một bộ chuyển đổi xác định cách lưu và khôi phục đối tượng. Saver được mô tả bằng một cặp lambda: save (chuyển đổi đối tượng thành loại có thể lưu trữ) và restore (khôi phục đối tượng từ dữ liệu đã lưu). Một Saver tiêu chuẩn đã được triển khai cho mutableStateListOf và mutableStateMapOf.
| Đặc điểm | remember | rememberSaveable |
|---|---|---|
| Giữ lại trong quá trình tái kết hợp | Có | Có |
| Giữ lại khi xoay | Không | Có |
| Giữ lại khi dừng tiến trình | Không | Có |
| Yêu cầu về loại | Bất kỳ | Parcelable, Serializable, Saver |
| Hiệu suất | Cao | Trung bình |
Hãy xem xét một kịch bản điển hình — màn hình chỉnh sửa hồ sơ nơi remember được sử dụng cho nhiều mục đích: lưu trữ trạng thái của các trường biểu mẫu, tính toán các giá trị dẫn xuất và lưu vào bộ nhớ đệm các thao tác tốn kém về mặt tính toán.
data class ProfileUiState(
val name: String = "",
val bio: String = "",
val isSaving: Boolean = false
)
@Composable
fun ProfileEditor() {
var state by rememberSaveable { mutableStateOf(ProfileUiState()) }
val isValid = remember {
derivedStateOf { state.name.isNotBlank() && state.bio.length() <= 500 }
}
val bioWarning = remember(state.bio) {
if (state.bio.length() > 400) {
"${500 - state.bio.length} chars left"
} else null
}
Column(modifier = Modifier.padding(16.dp)) {
OutlinedTextField(
value = state.name,
onValueChange = { state = state.copy(name = it) },
label = { Text("Tên") }
)
OutlinedTextField(
value = state.bio,
onValueChange = { state = state.copy(bio = it) },
label = { Text("Giới thiệu") }
)
bioWarning?.let { Text(it, color = MaterialTheme.colorScheme.error) }
Button(onClick = { /* save */ },
enabled = isValid.value) {
Text("Lưu")
}
}
}
Trong ví dụ, trạng thái được lưu qua rememberSaveable — văn bản sẽ không bị mất khi xoay màn hình. isValid được tính toán qua derivedStateOf, ngăn chặn các tính toán không cần thiết trong quá trình tái kết hợp. bioWarning sử dụng remember với khóa bio — đây là một tính toán tốn kém (tùy chọn, để minh họa) chỉ được tính toán lại khi bio thay đổi, không phải mỗi lần tái kết hợp.
Trạng thái dẫn xuất là các giá trị được tính toán từ các đối tượng State khác. Thay vì tính toán chúng ở mỗi lần tái kết hợp và lãng phí CPU cho các kết quả giống hệt nhau, remember với derivedStateOf chỉ tính toán giá trị khi các nguồn thay đổi. Điều này đặc biệt hữu ích cho việc lọc, sắp xếp và tổng hợp dữ liệu.
remember với khóa (remember(key) { calculation }) là một cơ chế tối ưu hóa khác. Nếu khóa không thay đổi kể từ lần tái kết hợp trước, khối tính toán sẽ không được thực thi và giá trị được lưu trong bộ nhớ đệm sẽ được trả về. Điều này thuận tiện cho việc lưu vào bộ nhớ đệm các đối tượng có việc tạo tốn kém: định dạng ngày tháng, phân tích JSON, tạo các bộ sưu tập bất biến lớn.
@Composable
fun SearchResults(allItems: List<Item>, query: String) {
// derivedStateOf: bộ lọc chỉ tính toán lại khi đầu vào thay đổi
val filtered = remember {
derivedStateOf {
allItems.filter { it.title.contains(query, true) }
}
}
// remember với khóa: thống kê được định dạng chỉ tính toán lại khi truy vấn thay đổi
val statsText = remember(query) {
"Results for query \"$query\": ${filtered.value.size}"
}
Text(statsText)
LazyColumn {
items(filtered.value, key = { it.id }) { item ->
Text(item.title)
}
}
}
filtered là một derivedStateOf tự động tính toán lại khi allItems hoặc query thay đổi. statsText sử dụng remember(query) — việc định dạng chuỗi tốn kém chỉ được thực hiện khi truy vấn tìm kiếm thay đổi. Sự kết hợp giữa derivedStateOf và remember với khóa mang lại hiệu suất tối đa: trạng thái dẫn xuất chỉ được tính toán khi cần thiết và các đối tượng phức tạp được lưu vào bộ nhớ đệm cho đến khi khóa thay đổi.
Câu Hỏi Thường Gặp
Không, remember là một hàm từ gói compose.runtime yêu cầu CompositionContext. Nó chỉ có thể được gọi bên trong hàm @Composable hoặc bên trong một hàm khác được gọi từ Composable. Để lưu trữ dữ liệu bên ngoài kết hợp, hãy sử dụng ViewModel.
Không có khóa, remember thực thi khối tính toán chỉ một lần — trong lần kết hợp đầu tiên. Tất cả các lần tái kết hợp tiếp theo trả về giá trị đã lưu. Nếu bạn cần tính toán lại giá trị khi dữ liệu thay đổi, hãy đảm bảo chỉ định chúng làm khóa: remember(data) { compute(data) }.
Không có cách trực tiếp để đặt lại remember. Cách duy nhất là loại bỏ hàm khỏi kết hợp (ví dụ: ẩn nó bằng điều kiện if) và sau đó hiển thị lại. Khi vào lại, khối remember sẽ thực thi lại, tạo ra một giá trị ban đầu mới.
remember lưu trữ trạng thái trong một slot của hàm Composable và tồn tại miễn là hàm còn trong kết hợp. ViewModel tồn tại miễn là vòng đời của màn hình còn. ViewModel được giữ lại khi xoay và được sử dụng cho logic nghiệp vụ. remember dành cho trạng thái UI cục bộ không cần thiết bên ngoài một hàm duy nhất.
Có, remember hoạt động chính xác trong các hàm @Preview Composable vì Preview tạo ra một CompositionContext đầy đủ. Tuy nhiên, rememberSaveable có thể không hoạt động chính xác trong Preview vì SavedStateHandle có thể không có trong môi trường xem trước.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm