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字段,插入数据库时提前计算并存储,映射时直接取值,避免每次字符串比较:
- 修改
DBPersonalOffer实体:
@Entity(tableName = DBPersonalOffer.TABLE_NAME) data class DBPersonalOffer( // 原有字段不变... @ColumnInfo(name = "is_plus") val isPlus: Boolean, )
- 更新
mapHttpResponse方法:
fun mapHttpResponse(response: NetworkPersonalOffer): DBPersonalOffer { return DBPersonalOffer( // 原有字段不变... isPlus = response.flowType == "Club Matas Plus" ) }
- 简化
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
相关产品推荐
相关产品推荐

