仅部分用户出现DateTimeFormatter解析崩溃问题求助
RSS订阅日期解析崩溃问题排查
本地运行时,使用DateTimeFormatter的排序功能正常,但部分用户反馈应用崩溃。所有RSS的pubDate均为英文格式(示例:Sat Aug 12 12:51:34 GMT+01:00 2023),且英美语系统用户都出现崩溃情况。
原代码实现
suspend fun sortDateTimeAndSaveLatestHeadline() { val dataStore = AppDataStore(getApplication()) val listOfStringDates: MutableList<String> = mutableListOf() val listOfHeadlines: MutableList<String> = mutableListOf() val listOfFeedTitles: MutableList<String> = mutableListOf() newsFeed.value?.forEach { feed -> listOfStringDates.add(feed.feedItem.pubDate) listOfHeadlines.add(feed.feedItem.title) listOfFeedTitles.add(feed.feedTitle) } val zippedLists = listOfStringDates.zip(listOfHeadlines) .zip(listOfFeedTitles) { (a, b), c -> Triple(a, b, c) } val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss O yyyy", Locale.ENGLISH) val result = zippedLists.sortedByDescending { LocalDateTime.parse(it.first, dateTimeFormatter) } if (result.isNotEmpty()) { dataStore.saveTileHeadline(result[0].second) dataStore.saveFeedTitle(result[0].third) println("Headline = ${result[0].second}") } }
崩溃堆栈信息
Exception java.time.format.DateTimeParseException: at java.time.format.DateTimeFormatter.parseResolved0 (DateTimeFormatter.java:1949) at java.time.format.DateTimeFormatter.parse (DateTimeFormatter.java:1851) at java.time.LocalDateTime.parse (LocalDateTime.java:486) at java.util.TimSort.countRunAndMakeAscending (TimSort.java:355) at java.util.TimSort.sort (TimSort.java:220) at java.util.Arrays.sort (Arrays.java:1424) at kotlin.collections.CollectionsKt___CollectionsKt.sortedWith (CollectionsKt___Collections.kt) at com.strangerweather.news.presentation.screens.screens.FeedItemsScreenKt$FeedItemsScreen$7$1.invoke (FeedItemsScreen.kt) at androidx.wear.compose.foundation.lazy.ScalingLazyColumnKt$ScalingLazyColumn$1$1$2$1.invoke (ScalingLazyColumn.kt) at androidx.compose.foundation.lazy.layout.LazyLayoutIntervalContent.<init> (LazyLayoutIntervalContent.java) at androidx.compose.foundation.lazy.LazyListIntervalContent.<init> (LazyListIntervalContent.java) at androidx.compose.runtime.snapshots.Snapshot$Companion.observe (Snapshot.java) at androidx.compose.runtime.DerivedSnapshotState.currentRecord (DerivedSnapshotState.java) at androidx.compose.runtime.snapshots.SnapshotKt.getLock (Snapshot.kt) at androidx.compose.runtime.DerivedSnapshotState$ResultRecord.readableHash (DerivedSnapshotState.java) at androidx.compose.runtime.snapshots.SnapshotKt.getLock (Snapshot.kt) at androidx.compose.runtime.DerivedSnapshotState$ResultRecord.isValid (DerivedSnapshotState.java) at androidx.compose.runtime.DerivedSnapshotState.currentRecord (DerivedSnapshotState.java) at androidx.compose.runtime.DerivedSnapshotState.getCurrentRecord (DerivedSnapshotState.java) at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.recordInvalidation (SnapshotStateObserver.java) at androidx.compose.runtime.snapshots.SnapshotStateObserver.drainChanges (SnapshotStateObserver.java) at androidx.compose.runtime.snapshots.SnapshotStateObserver.access$drainChanges (SnapshotStateObserver.java) at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot (Snapshot.kt) at androidx.compose.runtime.snapshots.SnapshotKt.advanceGlobalSnapshot (Snapshot.kt) at androidx.compose.runtime.snapshots.SnapshotKt.access$advanceGlobalSnapshot (Snapshot.kt) at androidx.compose.runtime.snapshots.GlobalSnapshot.notifyObjectsInitialized$runtime_release (GlobalSnapshot.java) at androidx.compose.runtime.DerivedSnapshotState.currentRecord (DerivedSnapshotState.java) at androidx.compose.runtime.snapshots.Snapshot$Companion.getCurrent (Snapshot.java) at androidx.compose.runtime.DerivedSnapshotState.getValue (DerivedSnapshotState.java) at androidx.wear.compose.foundation.lazy.ScalingLazyListState.getLayoutInfo (ScalingLazyListState.java) at androidx.wear.compose.foundation.lazy.ScalingLazyListState.scrollToItem$compose_foundation_release (ScalingLazyListState.java) at androidx.wear.compose.foundation.lazy.ScalingLazyListState.scrollToInitialItem$compose_foundation_release (ScalingLazyListState.java) at androidx.compose.wear.compose.foundation.lazy.ScalingLazyColumnKt$ScalingLazyColumn$1$1$3$1.invokeSuspend (ScalingLazyColumn.kt) at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith (BaseContinuationImpl.java) at kotlinx.coroutines.DispatchedTask.run (DispatchedTask.java) at androidx.compose.ui.platform.AndroidUiDispatcher.nextTask (AndroidUiDispatcher.java) at androidx.compose.ui.platform.AndroidUiDispatcher.performTrampolineDispatch (AndroidUiDispatcher.java) at androidx.compose.ui.platform.AndroidUiDispatcher.access$performTrampolineDispatch (AndroidUiDispatcher.java) at androidx.compose.ui.platform.AndroidUiDispatcher$dispatchCallback$1.run (AndroidUiDispatcher.java) at android.os.Handler.handleCallback (Handler.java:938) at android.os.Handler.dispatchMessage (Handler.java:99) at android.os.Looper.loop (Looper.java:246) at android.app.ActivityThread.main (ActivityThread.java:7690) at java.lang.reflect.Method.invoke at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:593) at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:995)
问题原因及修复方案
1. 时区格式匹配错误
原代码中使用的O格式符,匹配的是不带冒号的时区偏移(如GMT+1),但pubDate是GMT+01:00这种带冒号的格式,应该使用ZZZZ或XXX格式符:
ZZZZ:匹配GMT+01:00这种完整格式XXX:匹配+01:00这种偏移格式
2. 错误使用LocalDateTime解析带时区的日期
LocalDateTime不包含时区信息,解析带时区的字符串时,在部分系统环境下会出现解析失败。应该使用ZonedDateTime来处理带时区的日期,确保跨环境的兼容性。
3. 缺少异常防护
单个无效的日期字符串会导致整个排序流程崩溃,需要添加异常捕获来过滤无效条目。
修改后的代码
suspend fun sortDateTimeAndSaveLatestHeadline() { val dataStore = AppDataStore(getApplication()) val newsFeed = newsFeed.value ?: return // 简化列表生成逻辑,直接映射为Triple val zippedLists = newsFeed.map { feed -> Triple(feed.feedItem.pubDate, feed.feedItem.title, feed.feedTitle) } // 使用ZZZZ匹配带冒号的GMT时区格式 val dateTimeFormatter: DateTimeFormatter = DateTimeFormatter.ofPattern("EEE MMM dd HH:mm:ss ZZZZ yyyy", Locale.ENGLISH) // 过滤无效日期并排序 val result = zippedLists.filter { (dateStr) -> try { ZonedDateTime.parse(dateStr, dateTimeFormatter) true } catch (e: DateTimeParseException) { // 可在此处记录日志,追踪无效日期的来源 false } }.sortedByDescending { (dateStr) -> // 转成Instant保证跨时区的正确排序 ZonedDateTime.parse(dateStr, dateTimeFormatter).toInstant() } if (result.isNotEmpty()) { dataStore.saveTileHeadline(result[0].second) dataStore.saveFeedTitle(result[0].third) println("Headline = ${result[0].second}") } }
关键修改说明
- 简化了列表生成逻辑,避免创建多个可变列表,提升代码可读性
- 替换时区格式符为
ZZZZ,匹配pubDate的GMT+01:00格式 - 使用
ZonedDateTime解析带时区的日期,转成Instant进行排序,确保跨时区排序的正确性 - 添加
filter和异常捕获,过滤无效日期条目,避免单个错误导致整个流程崩溃
内容的提问来源于stack exchange,提问作者Code Poet
相关产品推荐
相关产品推荐

