当我从API下载数据时,我想显示一个装载指示器。但是,当这种情况发生时,指示器经常停止。我该如何改变它?或者是什么地方出错了?基本上,我获取出发时间并处理它们(例如,我将十六进制颜色转换为Jetpack Compose颜色,或者将unix日期转换为日期类型,等等),然后将它们装载到一个列表中并显示它们。
@Composable
fun StopScreen(
unixDate: Long? = null,
stopId: String,
viewModel: MainViewModel = hiltViewModel()
) {
LaunchedEffect(Unit) {
viewModel.getArrivalsAndDeparturesForStop(
unixDate,
stopId,
false
)
}
val isLoading by remember { viewModel.isLoading }
if (!isLoading) {
//showData
} else {
LoadingView()
}
}
@Composable
fun LoadingView() {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxSize()
) {
CircularProgressIndicator(color = MaterialTheme.colors.primary)
}
}
以及处理数据的视图模型:
@HiltViewModel
class MainViewModel @Inject constructor(
private val mainRepository: MainRepository
) : ViewModel() {
var stopTimesList = mutableStateOf<MutableList<StopTime>>(arrayListOf())
var alertsList = mutableStateOf<MutableList<Alert>>(arrayListOf())
var loadError = mutableStateOf("")
var isLoading = mutableStateOf(false)
var isRefreshing = mutableStateOf(false)
fun getArrivalsAndDeparturesForStop(unixDate: Long? = null, stopId: String, refresh: Boolean) {
viewModelScope.launch {
if (refresh) {
isRefreshing.value = true
} else {
isLoading.value = true
}
val result = mainRepository.getArrivalsAndDeparturesForStop(stopId = stopId, time = unixDate)
when (result) {
is Resource.Success -> {
//I temporarily store the data here, so that the screen is only refreshed on reload when all the new data has arrived and loaded
var preStopTimes: MutableList<StopTime> = arrayListOf()
var preAlertsList: MutableList<Alert> = arrayListOf()
if (result.data!!.stopTimes != null && result.data!!.alerts != null) {
var count = 0
val countAll =
result.data!!.stopTimes!!.count() + result.data!!.alertIds!!.count()
if (countAll == 0) {
loadError.value = ""
isLoading.value = false
isRefreshing.value = false
}
//ALERTS
for (alert in result.data!!.data.alerts) {
preAlertsList.add(alert)
count += 1
if (count == countAll) {
stopTimesList.value = preStopTimes
alertsList.value = preAlertsList
loadError.value = ""
isLoading.value = false
isRefreshing.value = false
}
}
for (stopTime in result.data!!.stopTimes!!) {
preStopTimes.add(stopTime)
count += 1
if (count == countAll) {
stopTimesList.value = preStopTimes
alertsList.value = preAlertsList
loadError.value = ""
isLoading.value = false
isRefreshing.value = false
}
}
} else {
loadError.value = "Error"
isLoading.value = false
isRefreshing.value = false
}
}
is Resource.Error -> {
loadError.value = result.message!!
isLoading.value = false
isRefreshing.value = false
}
}
}
}
}
储存库:
@ActivityScoped
class MainRepository @Inject constructor(
private val api: MainApi
) {
suspend fun getArrivalsAndDeparturesForStop(stopId: String,time: Long? = null): Resource<ArrivalsAndDeparturesForStop> {
val response = try {
api.getArrivalsAndDeparturesForStop(
stopId,
time
)
} catch (e: Exception) { return Resource.Error(e.message!!)}
return Resource.Success(response)
}
}
2条答案
按热度按时间k5ifujac1#
我认为你的Composable程序重组太频繁了,因为你在for循环中更新状态,否则,可能是因为你的
MainRepository
中的suspend方法没有在正确的线程中调度。我觉得您还没有掌握Compose的内部工作原理(这没关系,反正这是个新主题)。我建议您提升一个唯一的状态,而不是为所有属性设置几个可变状态。然后在VM内部构建它,当状态改变时通知视图。
大概是这样的:
请注意,我在保持原始结构的同时做了一些改进。
编辑:
你需要让你的
MainRepository
的suspend方法是main-safe的,它很可能运行在主线程(调用线程)上,因为你没有指定协程运行在哪个调度程序上。wgeznvg72#
6个月后,我找到了确切的解决方案。当我在ViewModel中处理数据时,所有的东西都在主线程上运行。进一步研究一下,我应该在函数中使用Dispatchers.Default和/或Dispatchers.IO来完成CPU密集型/列表排序/ JSON解析任务。
https://developer.android.com/kotlin/coroutines/coroutines-adv