如何在Jetpack Compose中展示Retrofit响应体数据?
在Jetpack Compose中展示Retrofit获取的QuoteList数据
我通过Retrofit请求获取到QuoteList格式的响应数据,已在Logcat中查看到数据内容,但不清楚如何在Jetpack Compose中解构这些数据并以文本形式展示(如Count、作者、引用内容)。我有JS开发背景,习惯用
result.count获取数据,想了解在Kotlin中如何访问result.body()内的数据。使用Android Studio生成的无Activity模板项目,添加了Retrofit相关代码,完整代码如下:
package com.example.retrofit1 import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.tooling.preview.Preview import com.example.retrofit1.ui.theme.Retrofit1Theme import android.util.Log import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch //import retrofit2.create import retrofit2.Response import retrofit2.http.GET import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory // retrofit try geeksforgeeks // data class QuoteList // according to JSON response // Retrofit interface //import retrofit2.http.Query interface QuotesApi { @GET("/quotes") suspend fun getQuotes() : Response<QuoteList> } object RetrofitHelper { val baseUrl = "https://quotable.io/" fun getInstance(): Retrofit { return Retrofit.Builder().baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) // we need to add converter factory to // convert JSON object to Java object .build() } } data class Result( val _id: String, val author: String, val authorSlug: String, val content: String, val dateAdded: String, val dateModified: String, val length: Int, val tags: List<String> ) data class QuoteList( val count: Int, val lastItemIndex: Int, val page: Int, val results: List<Result>, val totalCount: Int, val totalPages: Int ) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val quotesApi = RetrofitHelper.getInstance().create(QuotesApi::class.java) // launching a new coroutine GlobalScope.launch { val result = quotesApi.getQuotes() if (result != null) { // Checking the results Log.d("result: ", result.message().toString()) //debug // How to display count = 20 or lastItemIndex = 20 from the // QuoteList Log.d("count: ", result.body().toString()) //warning Log.w("myApp", "count --") } } setContent { Retrofit1Theme { // A surface container using the 'background' color from the theme Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { Greeting("Android") } } } } } @Composable fun Greeting(name: String, modifier: Modifier = Modifier) { Text( text = "Hello $name!", modifier = modifier ) } @Preview(showBackground = true) @Composable fun GreetingPreview() { Retrofit1Theme { Greeting("Android") } }
Logcat中看到的数据:
QuoteList(count=20, lastItemIndex=20, page=1, results=[Result(_id=bfNpGC2NI, author=Thomas Edison, authorSlug=thomas-edison, content=As a cure for worrying, work is better than whisky., dateAdded=2023-04-14, dateModified=2023-04-14, length=51, tags=[Humorous]),...etc
期望实现类似如下的展示效果:
Count : 20 Author : Thomas Edison Quote : "As a cure for worrying, work is better than whisky."
解决方案
核心要点
- 替换GlobalScope:用和Activity生命周期绑定的
lifecycleScope替代全局协程,避免内存泄漏 - 状态驱动UI:Compose依赖状态更新UI,需将获取到的
QuoteList纳入状态管理 - Kotlin空安全:
result.body()可能为null,需用?.或Elvis运算符处理 - 直接访问属性:Kotlin数据类的属性可通过
.直接访问,和JS用法一致
修改后的完整代码
package com.example.retrofit1 import android.os.Bundle import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import androidx.compose.ui.tooling.preview.Preview import com.example.retrofit1.ui.theme.Retrofit1Theme import android.util.Log import androidx.lifecycle.lifecycleScope import kotlinx.coroutines.launch import retrofit2.Response import retrofit2.http.GET import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory interface QuotesApi { @GET("/quotes") suspend fun getQuotes() : Response<QuoteList> } object RetrofitHelper { val baseUrl = "https://quotable.io/" fun getInstance(): Retrofit { return Retrofit.Builder().baseUrl(baseUrl) .addConverterFactory(GsonConverterFactory.create()) .build() } } data class Result( val _id: String, val author: String, val authorSlug: String, val content: String, val dateAdded: String, val dateModified: String, val length: Int, val tags: List<String> ) data class QuoteList( val count: Int, val lastItemIndex: Int, val page: Int, val results: List<Result>, val totalCount: Int, val totalPages: Int ) class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val quotesApi = RetrofitHelper.getInstance().create(QuotesApi::class.java) // 使用生命周期感知的协程 lifecycleScope.launch { val result = quotesApi.getQuotes() if (result.isSuccessful) { val quoteList = result.body() quoteList?.let { // 直接访问数据类属性,和JS的result.count用法一致 Log.d("count", it.count.toString()) Log.d("first quote", it.results.firstOrNull()?.content ?: "No quote found") // 更新UI setContent { Retrofit1Theme { Surface( modifier = Modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background ) { QuoteDisplay(quoteList = it) } } } } } else { Log.d("API Error", result.message()) } } } } @Composable fun QuoteDisplay(quoteList: QuoteList, modifier: Modifier = Modifier) { val firstResult = quoteList.results.firstOrNull() Column( modifier = modifier.padding(16.dp), verticalArrangement = Arrangement.Top ) { Text( text = "Count : ${quoteList.count}", style = MaterialTheme.typography.titleMedium ) Text( text = "Author : ${firstResult?.author ?: "Unknown"}", style = MaterialTheme.typography.bodyLarge, modifier = Modifier.padding(top = 8.dp) ) Text( text = "Quote : \"${firstResult?.content ?: "No quote available"}\"", style = MaterialTheme.typography.bodyMedium, modifier = Modifier.padding(top = 8.dp) ) } } @Preview(showBackground = true) @Composable fun QuoteDisplayPreview() { Retrofit1Theme { QuoteDisplay( quoteList = QuoteList( count = 20, lastItemIndex = 20, page = 1, results = listOf( Result( _id = "testId", author = "Thomas Edison", authorSlug = "thomas-edison", content = "As a cure for worrying, work is better than whisky.", dateAdded = "2023-04-14", dateModified = "2023-04-14", length = 51, tags = listOf("Humorous") ) ), totalCount = 100, totalPages = 5 ) ) } }
关键细节说明
- 属性访问:Kotlin数据类的属性直接通过
.访问,比如quoteList.count、firstResult?.author,和JS的result.count逻辑完全一致 - 空安全处理:用
?.安全调用属性,用?:提供默认值,避免空指针异常 - 生命周期协程:
lifecycleScope会在Activity销毁时自动取消协程,比GlobalScope更安全 - UI重组:获取到数据后调用
setContent更新UI,也可以用remember { mutableStateOf<QuoteList?>(null) }来持有状态,实现更灵活的UI更新
内容的提问来源于stack exchange,提问作者Marco
相关产品推荐
相关产品推荐

