在 LazyColumn 内查看 API 数据时显示消息(应用程序可能在其主线程上执行过多工作)

问题描述 投票:0回答:1

我试图将从 API 检索到的数据设置到 LazyColumn 中,每次从 Material3 日期选择器中选取日期时都会调用该数据,但在应用程序中,Logcat 中出现了这些警告跟踪(#1、#2)在每个 API 调用中。虽然我是在 LaunchedEffect 中实现的,但应用程序有时在每次 API 调用中都会花费太多时间(最多 15 秒)。

我附上了一些代码示例以供更多说明。

@Composable
fun TodayMatchesByLeague(matchesList: List<TodayResponseItem?>) {
    LazyColumn {
        items(matchesList.size) {
            TodayMatchItem(matchItem)
        }
    }
}
@Composable
fun FetchTodayGamesData(
    mainViewModel: MainViewModel = hiltViewModel()
) {
    val date = mainViewModel.dialogueDate.observeAsState().value!!
    if (mainViewModel.isClicked.value!!) {
        LaunchedEffect(key1 = null) {
            mainViewModel.getTodayMatches(
                date, 2023
            )
            mainViewModel.saveCalendarClicked(false)
        }
    }
    when (val state =
        mainViewModel.todayMatchesState.collectAsState().value
    ) {
        is TodayMatchesState.Empty -> {}
        is TodayMatchesState.Loading -> {}
        is TodayMatchesState.Error -> {}
        is TodayMatchesState.Success -> {
            TodayMatchesByLeague(
                matchesList = state.data.body()!!.response!!
            )
        }
    }
}
//
val selectedDate = rememberSaveable { mutableStateOf<LocalDate?>(LocalDate.now()) }
val localDate = selectedDate.value
viewModel.saveDialogueDate(localDate.toString())
val datePickerState = rememberDatePickerState()
var sheetState by remember{ mutableStateOf(false) }
val selectedDate1 = Instant.ofEpochMilli(datePickerState.selectedDateMillis ?: 0)
          .atZone(ZoneId.systemDefault())
          .toLocalDate()

if (datePickerState.selectedDateMillis != null) {
          selectedDate.value = selectedDate1
          viewModel.saveCalendarClicked(true)
}

if (sheetState)
          BottomSheetDatePicker(
                state = datePickerState,
                onDismissRequest = { sheetState = false }
          )
IconButton(
          onClick = {
                sheetState = true
          }
) {
          Icon(imageVector = Icons.Default.CalendarMonth, contentDescription = "Calendar")
}
//ViewModel
@HiltViewModel
class MainViewModel @Inject constructor(private val matchesRepository: MatchesRepository): ViewModel() {

    private var _todayMatchesState = MutableStateFlow<TodayMatchesState>(TodayMatchesState.Empty)
    val todayMatchesState: StateFlow<TodayMatchesState> = _todayMatchesState

    //Calendar Dialogue Date
    private var _dialogueDate = MutableLiveData("")
    val dialogueDate: LiveData<String> = _dialogueDate
    private var _isClicked = MutableLiveData(true)
    val isClicked: LiveData<Boolean> = _isClicked

    fun saveCalendarClicked(isClicked: Boolean) {
        _isClicked.value = isClicked
    }

    fun saveDialogueDate(date: String) {
        _dialogueDate.value = date
    }

    fun getTodayMatches(date: String, season: Int) {
        _todayMatchesState.value = TodayMatchesState.Loading

        viewModelScope.launch(Dispatchers.IO) {

            try {
                val todayMatchesResponse = matchesRepository.getTodayMatches(date, season)
                _todayMatchesState.value = TodayMatchesState.Success(todayMatchesResponse)
            }
            catch (exception: HttpException) {
                _todayMatchesState.value = TodayMatchesState.Error("Something went wrong")
            }
            catch (exception: IOException) {
                _todayMatchesState.value = TodayMatchesState.Error("No internet connection")
            }
        }
    }
}

#1

Event:APP_SCOUT_WARNING Thread:main backtrace:
    at java.lang.Thread.currentThread(Native Method)
    at java.lang.ThreadLocal.get(ThreadLocal.java:162)
    at android.os.Looper.myLooper(Looper.java:323)
    at kotlinx.coroutines.android.HandlerContext.isDispatchNeeded(HandlerDispatcher.kt:137)
    at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:160)
    at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:474)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:508)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:497)
    at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:368)
    at kotlinx.coroutines.flow.StateFlowSlot.makePending(StateFlow.kt:284)
    at kotlinx.coroutines.flow.StateFlowImpl.updateState(StateFlow.kt:349)
    at kotlinx.coroutines.flow.StateFlowImpl.setValue(StateFlow.kt:316)
    at coil.compose.ConstraintsSizeResolver.measure-3p2s80s(AsyncImage.kt:209)
    at androidx.compose.ui.node.BackwardsCompatNode.measure-3p2s80s(BackwardsCompatNode.kt:312)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:186)
    at androidx.compose.foundation.layout.SizeNode.measure-3p2s80s(Size.kt:838)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:186)
    at androidx.compose.foundation.layout.PaddingNode.measure-3p2s80s(Padding.kt:397)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:186)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:255)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:254)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:488)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:501)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:257)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeMeasureSnapshotReads$ui_release(OwnerSnapshotObserver.kt:113)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:1622)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate.access$performMeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:39)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.remeasure-BRTryo0(LayoutNodeLayoutDelegate.kt:623)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$MeasurePassDelegate.measure-BRTryo0(LayoutNodeLayoutDelegate.kt:599)
    at androidx.compose.foundation.layout.RowColumnMeasurementHelper.measureWithoutPlacing-_EkL_-Y(RowColumnMeasurementHelper.kt:120)
    at androidx.compose.foundation.layout.RowColumnMeasurePolicy.measure-3p2s80s(RowColumnImpl.kt:69)
    at androidx.compose.ui.node.InnerNodeCoordinator.measure-BRTryo0(InnerNodeCoordinator.kt:134)
    at androidx.compose.foundation.layout.PaddingNode.measure-3p2s80s(Padding.kt:397)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.measure-BRTryo0(LayoutModifierNodeCoordinator.kt:186)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:255)
    at androidx.compose.ui.node.LayoutNodeLayoutDelegate$performMeasureBlock$1.invoke(LayoutNodeLayoutDelegate.kt:254)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:488)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:501)

#2

Event:APP_SCOUT_HANG Thread:main backtrace:
    at java.lang.ref.Reference$SinkHolder.-$$Nest$sfgetfinalize_count(Unknown Source:2)
    at java.lang.ref.Reference.reachabilityFence(Reference.java:342)
    at libcore.util.NativeAllocationRegistry.registerNativeAllocation(NativeAllocationRegistry.java:281)
    at android.graphics.ColorFilter.getNativeInstance(ColorFilter.java:68)
    at android.graphics.Paint.getNativeInstance(Paint.java:738)
    at android.graphics.BaseRecordingCanvas.drawBitmap(BaseRecordingCanvas.java:95)
    at androidx.compose.ui.graphics.AndroidCanvas.drawImageRect-HPBpro0(AndroidCanvas.android.kt:275)
    at androidx.compose.ui.graphics.drawscope.CanvasDrawScope.drawImage-AZ2fEMs(CanvasDrawScope.kt:257)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawImage-AZ2fEMs(Unknown Source:24)
    at androidx.compose.ui.graphics.drawscope.DrawScope.drawImage-AZ2fEMs$default(DrawScope.kt:566)
    at androidx.compose.ui.graphics.vector.DrawCache.drawInto(DrawCache.kt:102)
    at androidx.compose.ui.graphics.vector.VectorComponent.draw(Vector.kt:181)
    at androidx.compose.ui.graphics.vector.VectorPainter.onDraw(VectorPainter.kt:248)
    at androidx.compose.ui.graphics.painter.Painter.draw-x_KDEd0(Painter.kt:212)
    at androidx.compose.ui.draw.PainterNode.draw(PainterModifier.kt:342)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:105)
    at androidx.compose.ui.node.LayoutNodeDrawScope.draw-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:86)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:376)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:365)
    at androidx.compose.ui.node.LayoutModifierNodeCoordinator.performDraw(LayoutModifierNodeCoordinator.kt:265)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:373)
    at androidx.compose.ui.node.NodeCoordinator.draw(NodeCoordinator.kt:365)
    at androidx.compose.ui.node.LayoutNode.draw$ui_release(LayoutNode.kt:962)
    at androidx.compose.ui.node.InnerNodeCoordinator.performDraw(InnerNodeCoordinator.kt:183)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:66)
    at androidx.compose.material.ripple.AndroidRippleIndicationInstance.drawIndication(Ripple.android.kt:270)
    at androidx.compose.foundation.IndicationModifier.draw(Indication.kt:346)
    at androidx.compose.ui.node.BackwardsCompatNode.draw(BackwardsCompatNode.kt:350)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:105)
    at androidx.compose.ui.node.LayoutNodeDrawScope.performDraw(LayoutNodeDrawScope.kt:76)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawContent(LayoutNodeDrawScope.kt:55)
    at androidx.compose.foundation.BackgroundNode.draw(Background.kt:159)
    at androidx.compose.ui.node.LayoutNodeDrawScope.drawDirect-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:105)
    at androidx.compose.ui.node.LayoutNodeDrawScope.draw-x_KDEd0$ui_release(LayoutNodeDrawScope.kt:86)
    at androidx.compose.ui.node.NodeCoordinator.drawContainedDrawModifiers(NodeCoordinator.kt:376)
    at androidx.compose.ui.node.NodeCoordinator.access$drawContainedDrawModifiers(NodeCoordinator.kt:56)
    at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:395)
    at androidx.compose.ui.node.NodeCoordinator$drawBlock$1$1.invoke(NodeCoordinator.kt:394)
    at androidx.compose.runtime.snapshots.Snapshot$Companion.observe(Snapshot.kt:488)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver$ObservedScopeMap.observe(SnapshotStateObserver.kt:501)
    at androidx.compose.runtime.snapshots.SnapshotStateObserver.observeReads(SnapshotStateObserver.kt:257)
    at androidx.compose.ui.node.OwnerSnapshotObserver.observeReads$ui_release(OwnerSnapshotObserver.kt:133)
    at androidx.compose.ui.node.NodeCoordinator$drawBlock$1.invoke(NodeCoordinator.kt:394)

android android-jetpack-compose retrofit2 kotlin-coroutines
1个回答
0
投票

请首先确保 API 不是这里的瓶颈。您可以使用 PostManThunderclient Visual Studio Code 插件向您的 API 发送请求。使用这些工具,您可以检查 API 本身是否需要那么多时间来返回响应。

一旦您解决了这个可能的原因,我建议您附加一个调试器并在

LaunchedEffect
中的代码中设置一个断点。我在你的代码中没有看到明显的错误,但有些味道:

1.)

LaunchedEffect
viewModelScope
是多余的

您同时使用

viewModelScope
LaunchedEffect
来调用
getTodayMatches
函数。如果使用
LaunchedEffect
,则可以直接调用挂起函数,而不需要
viewModelScope
。所以你可以像这样更新你的 ViewModel 函数:

suspend fun getTodayMatches(date: String, season: Int) {
    _todayMatchesState.value = TodayMatchesState.Loading

    try {
        val todayMatchesResponse = matchesRepository.getTodayMatches(date, season)
        _todayMatchesState.value = TodayMatchesState.Success(todayMatchesResponse)
    }
    catch (exception: HttpException) {
        _todayMatchesState.value = TodayMatchesState.Error("Something went wrong")
    }
    catch (exception: IOException) {
        _todayMatchesState.value = TodayMatchesState.Error("No internet connection")
    }
}

2.) 回调处理看起来很奇怪

确认 DatePicker 后,将 ViewModel 中的

isClicked
变量设置为 true。然后,当变量变为 true 时,您可以在另一个地方使用副作用来执行代码。我建议您在所选日期更改时直接调用 ViewModel 函数,而不是这种方法。

val selectedDate by rememberSaveable { mutableStateOf<LocalDate?>(LocalDate.now()) }
val datePickerState = rememberDatePickerState()
var sheetState by remember{ mutableStateOf(false) }

// Whenever selected date changes, call getTodayMatches
LaunchedEffect(datePickerState.selectedDateMillis) {
    if (datePickerState.selectedDateMillis != null) {
        selectedDate = Instant.ofEpochMilli(datePickerState.selectedDateMillis ?: 0)
            .atZone(ZoneId.systemDefault())
            .toLocalDate()
        viewModel.getTodayMatches(selectedDate.toString, 2023)
    }
}

if (sheetState)

    BottomSheetDatePicker(
        // ...
    )
    // ...
}

3.) LazyColumn 看起来很奇怪

您似乎正在混合

items
函数的两个重载。你可能想做

LazyColumn {
    items(matchesList) { matchItem ->
        TodayMatchItem(matchItem)
    }
}

4.) 单一事实来源

您的 Composable 和 ViewModel 似乎有很多重复。将日期存储在 ViewModel 中,或者将其存储在 Composable 中。但现在,您的 Composable 中有一个

selectedDate
变量,同时您的 ViewModel 中有一个
dialogueDate

© www.soinside.com 2019 - 2024. All rights reserved.