使用Retrofit发起HTTP请求时Composable视图崩溃求助
Kotlin Retrofit + Compose 请求数据崩溃问题
每次加载SampleView()组合函数时应用崩溃,以下是我向本地Node.js API发起请求的代码:
SampleView 代码
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 7) @Composable fun SampleView() { val data: ArrayOfSampleData = loadData() Text(text = data.data.str) } @OptIn(DelicateCoroutinesApi::class) @RequiresExtension(extension = Build.VERSION_CODES.S, version = 7) fun loadData(): ArrayOfSampleData { var data: ArrayOfSampleData = (ArrayOfSampleData((SampleDataClass(id = 0, str = "", bools = arrayOf(0))))) GlobalScope.launch(Dispatchers.IO) { val response = try { RetrofitInstance.api.loadData() } catch (e: HttpException) { print(e.message) return@launch } catch (e: IOException) { print(e.message) return@launch } if (response.isSuccessful && response.body() != null) { withContext(Dispatchers.Main) { data = response.body()!! } } } return data }
Retrofit 配置代码
object RetrofitInstance { private val client = OkHttpClient.Builder().apply { addInterceptor(Interceptor()) }.build() val api : ApiInterface by lazy { Retrofit.Builder() .baseUrl(Util.Base) .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() .create(ApiInterface::class.java) } } class Interceptor: Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val request = chain.request() .newBuilder() .addHeader("Content-Type", "application/json") .addHeader("apikey", "apikey") .build() return chain.proceed(request) } } interface ApiInterface { @GET("/test") suspend fun loadData(): Response<ArrayOfSampleData>; } object Util { const val Base = "localhost:4300" }
数据类代码
data class SampleDataClass( val id: Int, val str: String, val bools: Array<Int> ) data class ArrayOfSampleData( val data: Array<SampleDataClass> )
崩溃原因及修正方案
数组属性访问错误
原代码中data.data.str直接访问数组的str属性,这是错误的——data.data是Array<SampleDataClass>类型,必须通过索引访问元素后再获取属性,比如data.data[0].str。这是直接导致崩溃的原因。异步请求未结合Compose状态管理
loadData()是同步函数,但内部用GlobalScope.launch发起异步请求,直接返回初始值,且修改data变量无法触发Compose重组。需改用mutableStateOf保存状态,结合LaunchedEffect在组件生命周期内发起请求。GlobalScope不当使用
GlobalScope的生命周期不受Compose组件控制,易导致内存泄漏。应使用rememberCoroutineScope或LaunchedEffect提供的协程作用域。BaseUrl缺少协议
Retrofit要求BaseUrl必须包含http://或https://,原代码中localhost:4300会导致请求失败。类名冲突
自定义的Interceptor类与OkHttp的Interceptor接口重名,易引发编译或运行时混淆,需重命名自定义拦截器。
修正后的完整代码
SampleView 修正版
@RequiresExtension(extension = Build.VERSION_CODES.S, version = 7) @Composable fun SampleView() { // 用mutableStateOf保存数据状态,触发Compose重组 val dataState = remember { mutableStateOf<ArrayOfSampleData?>(null) } // 组件首次加载时发起请求 LaunchedEffect(Unit) { val response = try { RetrofitInstance.api.loadData() } catch (e: HttpException) { e.printStackTrace() return@LaunchedEffect } catch (e: IOException) { e.printStackTrace() return@LaunchedEffect } if (response.isSuccessful && response.body() != null) { dataState.value = response.body() } } // 根据状态显示不同内容 val data = dataState.value when { data == null -> Text(text = "加载中...") data.data.isEmpty() -> Text(text = "无数据") else -> Text(text = data.data[0].str) } }
Retrofit 配置修正版
object RetrofitInstance { private val client = OkHttpClient.Builder().apply { addInterceptor(AuthInterceptor()) // 重命名拦截器避免冲突 }.build() val api : ApiInterface by lazy { Retrofit.Builder() .baseUrl("http://${Util.Base}") // 添加http协议 .client(client) .addConverterFactory(GsonConverterFactory.create()) .build() .create(ApiInterface::class.java) } } // 重命名为AuthInterceptor,避免与OkHttp的Interceptor冲突 class AuthInterceptor : okhttp3.Interceptor { override fun intercept(chain: okhttp3.Interceptor.Chain): okhttp3.Response { val request = chain.request() .newBuilder() .addHeader("Content-Type", "application/json") .addHeader("apikey", "apikey") .build() return chain.proceed(request) } } interface ApiInterface { @GET("/test") suspend fun loadData(): Response<ArrayOfSampleData> } object Util { const val Base = "localhost:4300" }
数据类修正版(改用List替代Array,提升Gson兼容性)
data class SampleDataClass( val id: Int, val str: String, val bools: List<Int> ) data class ArrayOfSampleData( val data: List<SampleDataClass> )
内容的提问来源于stack exchange,提问作者mfusco
相关产品推荐
相关产品推荐

