Kotlin开发汇率兑换APP遭遇IndexOutOfBoundsException错误求助
解决汇率兑换应用的IndexOutOfBoundsException异常
在开发汇率兑换应用时,尝试获取首个货币数据触发IndexOutOfBoundsException,原因是exchangeDetail数组为空时直接访问了索引0。相关代码如下:
相关代码片段
CalculateView代码
Column { if(calculateViewModel.loading){ CircularProgressIndicator() }else{ Text(text = calculateViewModel.exchangeDetail[0].get("EUR").toString()) Text(text = calculateViewModel.exchangeDetail[0].get(currency2).toString()) } }
CalculateViewModel代码
class CalculateViewModel: ViewModel() { private val exchangeService = ExchangeService() var loading: Boolean by mutableStateOf(false) var errorMessage : String by mutableStateOf("") var exchangeDetail : MutableList<Map<String, Float>> by mutableStateOf(mutableListOf()) fun getExchangeRatio(input1: String, input2: String) = viewModelScope.launch { errorMessage = "" loading = true try { val ratio = exchangeService.getExchangeCourse(input1,input2) exchangeDetail.add(ratio.data) }catch (e: Exception){ errorMessage = e.message.toString() }finally { loading = false } } }
ExchangeDto类代码
@kotlinx.serialization.Serializable data class ExchangeDto ( val data: Map<String, Float> //Zugriff über .get() )
APIManager代码
class APIManager { private val apiKey = "Om1xoJkPAxk9neFbFvzezQtxAuNHdSERTfN8CPbk" var jsonHttpClient = HttpClient { expectSuccess = true install(ContentNegotiation) { json(Json { ignoreUnknownKeys = true }) } defaultRequest { url.host = "api.freecurrencyapi.com" url.protocol = URLProtocol.HTTPS url.encodedPath = "/v1/latest?apikey=$apiKey" + url.encodedPath contentType(ContentType.Application.Json) headers { append(HttpHeaders.Authorization, "Bearer $apiKey") } } HttpResponseValidator { getCustomResponseValidator(this) } } }
核心问题分析
直接访问exchangeDetail[0]未做空安全校验:
- 首次调用API前,
exchangeDetail是空列表 - API请求失败时,
exchangeDetail不会被填充 - 即使请求成功,也需先确认列表有元素再访问索引
修复步骤
1. 优化View层的空安全与错误处理
修改CalculateView代码,增加空判断、错误状态展示,同时处理Map取值可能为null的情况:
Column { if(calculateViewModel.loading){ CircularProgressIndicator() }else { // 展示错误信息 calculateViewModel.errorMessage.takeIf { it.isNotEmpty() }?.let { Text(text = "请求失败: $it", color = Color.Red) } ?: run { // 判断列表是否有数据 calculateViewModel.exchangeDetail.firstOrNull()?.let { dataMap -> // 处理Map取值为null的情况 val eurValue = dataMap.get("EUR")?.toString() ?: "无数据" val currency2Value = dataMap.get(currency2)?.toString() ?: "无数据" Text(text = eurValue) Text(text = currency2Value) } ?: Text(text = "暂无汇率数据") } } }
2. ViewModel层优化(可选)
若无需存储历史汇率数据,没必要用MutableList<Map<String, Float>>,直接用单个Map的State更简洁,避免列表操作问题:
class CalculateViewModel: ViewModel() { private val exchangeService = ExchangeService() var loading: Boolean by mutableStateOf(false) var errorMessage : String by mutableStateOf("") // 改为单个Map,初始为空 var exchangeData : Map<String, Float> by mutableStateOf(emptyMap()) fun getExchangeRatio(input1: String, input2: String) = viewModelScope.launch { errorMessage = "" loading = true try { val ratio = exchangeService.getExchangeCourse(input1,input2) exchangeData = ratio.data }catch (e: Exception){ errorMessage = e.message.toString() // 请求失败时清空数据,避免旧数据残留 exchangeData = emptyMap() }finally { loading = false } } }
对应的View层可简化为:
Column { if(calculateViewModel.loading){ CircularProgressIndicator() }else { calculateViewModel.errorMessage.takeIf { it.isNotEmpty() }?.let { Text(text = "请求失败: $it", color = Color.Red) } ?: run { if(calculateViewModel.exchangeData.isNotEmpty()){ val eurValue = calculateViewModel.exchangeData.get("EUR")?.toString() ?: "无数据" val currency2Value = calculateViewModel.exchangeData.get(currency2)?.toString() ?: "无数据" Text(text = eurValue) Text(text = currency2Value) }else{ Text(text = "暂无汇率数据") } } } }
3. 额外建议
- 在ViewModel的
try块中打印ratio.data,确认API返回的货币键值对是否符合预期 - 针对
get()方法返回null的场景,始终设置默认值,避免空指针异常
内容的提问来源于stack exchange,提问作者Salih Kut
相关产品推荐
相关产品推荐

