DayCell SolarDay 静态缓存:避免 Pager 切换时重复创建对象触发 GC

每个 DayCell 创建时调用两次 SolarDay.fromYmd() 计算节日/农历信息。
Pager 缓存页的大量 DayCell 同时重建时产生大量临时对象,加剧 GC 压力
(trace 中 Background concurrent mark compact GC 346ms+253ms)。

修复:在 DayCell.kt 中增加进程级静态缓存 dayCellInfoCache,按日期缓存
computeDayCellInfo() 的结果。首次计算后永久复用,消除重复对象创建。
This commit is contained in:
meyou 2026-05-18 23:08:12 +08:00
parent fab0a5eba8
commit c8ee7f7b44
No known key found for this signature in database

View File

@ -38,6 +38,44 @@ import com.tyme.solar.SolarDay
import kotlinx.datetime.LocalDate
import plus.rua.project.ShiftKind
// P0-C: 静态缓存 SolarDay 计算结果,避免 Pager 滑动/切换时重复创建对象触发 GC
@Suppress("DEPRECATION") // monthNumber 无替代 API
private fun computeDayCellInfo(date: LocalDate): Triple<String, Boolean, String?> {
val solarDay = SolarDay.fromYmd(date.year, date.monthNumber, date.day)
val holidayBadge = solarDay.getLegalHoliday()?.let { if (it.isWork()) "" else "" }
val lunarDay = solarDay.getLunarDay()
// 农历传统节日(仅当天)
val lunarFestival = lunarDay.getFestival()
if (lunarFestival != null) {
return Triple(lunarFestival.getName(), true, holidayBadge)
}
// 节气(当天才显示)
val termDay = solarDay.getTermDay()
if (termDay.getDayIndex() == 0) {
return Triple(termDay.getSolarTerm().getName(), true, holidayBadge)
}
// 公历节日(仅当天)
val solarFestival = solarDay.getFestival()
if (solarFestival != null) {
return Triple(solarFestival.getName(), true, holidayBadge)
}
// 默认:农历日期
val name = lunarDay.getName()
val text = if (name == "初一") {
val lunarMonth = lunarDay.getLunarMonth()
"${lunarMonth.getName()}"
} else {
name
}
return Triple(text, false, holidayBadge)
}
private val dayCellInfoCache = mutableMapOf<LocalDate, Triple<String, Boolean, String?>>()
enum class DayCellState {
NORMAL, OTHER_MONTH, TODAY, SELECTED, SELECTED_TODAY
}
@ -124,53 +162,16 @@ fun DayCell(
val selectedOutlineColor = MaterialTheme.colorScheme.primary
data class DayAnnotation(val text: String, val isHighlight: Boolean)
val holidayBadge = remember(date) {
@Suppress("DEPRECATION") // monthNumber 无替代 API
val solarDay = SolarDay.fromYmd(date.year, date.monthNumber, date.day)
solarDay.getLegalHoliday()?.let { if (it.isWork()) "" else "" }
}
val annotation = remember(date) {
@Suppress("DEPRECATION") // monthNumber 无替代 API
val solarDay = SolarDay.fromYmd(date.year, date.monthNumber, date.day)
val lunarDay = solarDay.getLunarDay()
// 农历传统节日(仅当天)
val lunarFestival = lunarDay.getFestival()
if (lunarFestival != null) {
return@remember DayAnnotation(lunarFestival.getName(), true)
}
// 节气(当天才显示)
val termDay = solarDay.getTermDay()
if (termDay.getDayIndex() == 0) {
return@remember DayAnnotation(termDay.getSolarTerm().getName(), true)
}
// 公历节日(仅当天)
val solarFestival = solarDay.getFestival()
if (solarFestival != null) {
return@remember DayAnnotation(solarFestival.getName(), true)
}
// 默认:农历日期
val name = lunarDay.getName()
val text = if (name == "初一") {
val lunarMonth = lunarDay.getLunarMonth()
"${lunarMonth.getName()}"
} else {
name
}
DayAnnotation(text, false)
// P0-C: 使用静态缓存避免每次重组时重复创建 SolarDay 对象
val (annotationText, isAnnotationHighlight, holidayBadge) = remember(date) {
dayCellInfoCache.getOrPut(date) { computeDayCellInfo(date) }
}
val lunarColor by transition.animateColor(
transitionSpec = { tween(150, easing = FastOutSlowInEasing) },
label = "lunarColor"
) { state ->
if (annotation.isHighlight) {
if (isAnnotationHighlight) {
when (state) {
DayCellState.SELECTED_TODAY -> MaterialTheme.colorScheme.onPrimaryContainer.copy(
alpha = 0.85f
@ -250,7 +251,7 @@ fun DayCell(
style = MaterialTheme.typography.bodyMedium
)
Text(
text = annotation.text,
text = annotationText,
textAlign = TextAlign.Center,
color = lunarColor,
fontSize = 7.sp,