You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin中PersonalOffer列表处理耗时过长的优化咨询

PersonalOffer模块性能优化方案

我维护的项目中,其他API的响应展示均在2秒以内,但PersonalOffer模块展示耗时过长,推测问题出在排序逻辑及重复映射操作上,相关背景描述如下:

所有其他API的响应展示均在2秒以内,但PersonalOffer的展示耗时过长。我认为问题出在以下排序逻辑中,同时映射操作也被执行了多次。

原始核心函数代码:

fun getPersonalOffers(): Flow<List<PersonalOffer>> {
        return personalOfferDao.getPersonalOffers().map { list ->
            list.mapNotNull { PersonalOffer.mapFromDB(it) }.sortedBy { it.expiryDate }.sortedByDescending { it.isPlus }
        }
    }

针对上述代码,可从以下几个方向进行优化:

1. 合并排序逻辑,减少遍历次数

当前代码连续调用sortedBy和sortedByDescending,会触发两次完整的列表遍历排序,且后一次排序会覆盖前一次的部分结果。改用sortedWith结合多条件比较器,一次完成排序:

// 替换原排序代码,先按isPlus降序,再按expiryDate升序
list.mapNotNull { PersonalOffer.mapFromDB(it) }.sortedWith(
    compareByDescending<PersonalOffer> { it.isPlus }.thenBy { it.expiryDate }
)

2. 缓存日期格式化器,避免重复创建

PersonalOffer.mapFromDB方法中每次都会新建SimpleDateFormat实例,这是高频耗时操作。将其改为静态常量缓存:

companion object {
    // 静态缓存日期格式化器,避免重复初始化
    private val DATE_FORMATTER = SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.UK)
    
    fun mapFromDB(dbPersonalOffer: DBPersonalOffer?): PersonalOffer? {
        dbPersonalOffer?.let {
            var expiryDate: Date? = try {
                DATE_FORMATTER.parse(it.expiryDate)
            } catch (ex: Exception) {
                null
            }
            // 其余映射逻辑保持不变
            return PersonalOffer(
                id = it.id,
                disclaimerText = it.disclaimerText,
                discountName = it.discountName,
                discountPercent = it.discountPercent,
                expiryDate = expiryDate,
                futureTeaserText = it.futureTeaserText,
                prizeId = it.prizeId,
                productName = it.productName,
                teaserText = it.teaserText,
                isPlus = it.flowType == "Club Matas Plus",
                completeText = it.completeText ?: "",
                flowType = it.flowType,
                images = PersonalOfferImages.mapFromDB(it.images),
                extraInfoText = it.extraInfoText
            )
        } ?: run {
            return null
        }
    }
}

如果项目支持Android API 26+,推荐使用线程安全的DateTimeFormatter替代SimpleDateFormat:

private val DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss", Locale.UK)

// 解析逻辑替换为:
var expiryDate: Date? = try {
    Date.from(LocalDateTime.parse(it.expiryDate, DATE_FORMATTER).atZone(ZoneId.systemDefault()).toInstant())
} catch (ex: Exception) {
    null
}

3. 数据库层面预排序,降低内存开销

既然最终需要固定排序规则,直接在DAO查询中完成排序,从数据库获取的就是已排好序的列表,无需在内存中二次排序:
修改PersonalOfferDao.kt的查询语句:

@Query("SELECT * FROM personal_offer_table ORDER BY CASE WHEN flowType = 'Club Matas Plus' THEN 0 ELSE 1 END, expiryDate ASC")
fun getPersonalOffers(): Flow<List<DBPersonalOffer>>

通过CASE表达式将flowType为"Club Matas Plus"的记录优先返回,再按expiryDate升序排列,彻底省去内存排序步骤。

4. 提前计算isPlus字段,避免重复字符串比较

在DBPersonalOffer中新增isPlus字段,插入数据库时提前计算并存储,映射时直接取值,避免每次字符串比较:

  1. 修改DBPersonalOffer实体:
@Entity(tableName = DBPersonalOffer.TABLE_NAME)
data class DBPersonalOffer(
    // 原有字段不变...
    @ColumnInfo(name = "is_plus")
    val isPlus: Boolean,
)
  1. 更新mapHttpResponse方法:
fun mapHttpResponse(response: NetworkPersonalOffer): DBPersonalOffer {
    return DBPersonalOffer(
        // 原有字段不变...
        isPlus = response.flowType == "Club Matas Plus"
    )
}
  1. 简化PersonalOffer.mapFromDB中的取值:
isPlus = it.isPlus

优化后的完整getPersonalOffers函数

结合数据库预排序优化后,函数可简化为:

fun getPersonalOffers(): Flow<List<PersonalOffer>> {
    return personalOfferDao.getPersonalOffers().map { list ->
        list.mapNotNull { PersonalOffer.mapFromDB(it) }
    }
}

内容的提问来源于stack exchange,提问作者Taimoor Khan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 07:58:13