You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Retrofit获取数据为空问题排查求助(附完整代码)

货币应用Retrofit请求数据为空排查

我正在开发一款货币类应用,使用Retrofit接口请求数据时,收到的是空数据,但返回的JSON格式是正常的。调试后无法定位错误位置及原因,附上所有相关代码,希望能得到帮助排查问题。


相关代码

ApiService

interface ApiService {
    @GET("daily_json.js")
    suspend fun getValute(): Response<Valute>
}

RetrofitInstance

object RetrofitInstance {
    private val retrofit by lazy {
        Retrofit.Builder().baseUrl("https://www.cbr-xml-daily.ru/")
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }
    val api: ApiService by lazy {
        retrofit.create(ApiService::class.java)
    }
}

Repository

class Repository {
    suspend fun getVal(): Response<Valute>{
        return RetrofitInstance.api.getValute()
    }
}

ViewModel

class StartViewModel: ViewModel() {
    var repo = Repository()
    val moneyList: MutableLiveData<Response<Valute>> = MutableLiveData()

    fun getMoney(){
        viewModelScope.launch {
            moneyList.value = repo.getVal()
        }
    }
}

Fragment

class StartFragment : Fragment() {
    private lateinit var _binding: FragmentStartBinding
    private val binding get() = _binding!!
    private val adapter = StartAdapter()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentStartBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.rvStart.adapter = adapter
        val viewModel = ViewModelProvider(this).get(StartViewModel::class.java)
        viewModel.getMoney()
        viewModel.moneyList.observe(viewLifecycleOwner,{list ->
            list.body()?.let { adapter.setList(it) }
        })
    }
}

Adapter

class StartAdapter: RecyclerView.Adapter<StartAdapter.StartHolder>() {
    var listValute = emptyList<Valute>()

    class StartHolder(item: View): RecyclerView.ViewHolder(item) {
        val binding = ItemBinding.bind(item)
        fun bind(valute: Valute) = with(binding){
            nameMoney.text = valute.CharCode
            purchase.text = valute.Value.toString()
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StartHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item, parent, false)
        return StartHolder(view)
    }

    override fun getItemCount(): Int {
        return listValute.size
    }

    override fun onBindViewHolder(holder: StartHolder, position: Int) {
        holder.bind(listValute[position])
    }

    fun setList(valute: Valute){
        listValute = listOf(valute)
        notifyDataSetChanged()
    }
}

补充数据类

data class Valute(
    val CharCode: String,
    val ID: String,
    val Name: String,
    val Nominal: Int,
    val NumCode: String,
    val Previous: Double,
    val Value: Double
): Serializable

问题排查与修复

核心错误点

  1. JSON结构不匹配:目标接口https://www.cbr-xml-daily.ru/daily_json.js的返回外层是一个包含Valute字段的对象,Valute内部是多个货币的键值对(如USD、EUR对应单个Valute实例),但你的接口直接声明返回Response<Valute>,Gson无法将整个JSON解析为单个Valute对象,导致解析后数据为空。
  2. Adapter数据处理错误:即使拿到正确数据,你的setList方法将单个Valute转为列表,而实际需要的是多个Valute的集合,RecyclerView无法正确展示数据。

修复步骤

1. 新增外层数据类匹配JSON结构

data class CurrencyResponse(
    val Date: String,
    val PreviousDate: String,
    val PreviousURL: String,
    val Timestamp: String,
    val Valute: Map<String, Valute>
): Serializable

2. 修改ApiService返回类型

interface ApiService {
    @GET("daily_json.js")
    suspend fun getValute(): Response<CurrencyResponse>
}

3. 更新Repository和ViewModel的返回类型

Repository:

class Repository {
    suspend fun getVal(): Response<CurrencyResponse>{
        return RetrofitInstance.api.getValute()
    }
}

ViewModel:

class StartViewModel: ViewModel() {
    var repo = Repository()
    val moneyList: MutableLiveData<Response<CurrencyResponse>> = MutableLiveData()

    fun getMoney(){
        viewModelScope.launch {
            moneyList.value = repo.getVal()
        }
    }
}

4. 调整Fragment的数据解析逻辑

将Valute的Map转为List传给Adapter:

viewModel.moneyList.observe(viewLifecycleOwner, { response ->
    response.body()?.let { currencyResponse ->
        val valuteList = currencyResponse.Valute.values.toList()
        adapter.setList(valuteList)
    }
})

5. 修改Adapter的setList方法

fun setList(valuteList: List<Valute>){
    listValute = valuteList
    notifyDataSetChanged()
}

额外调试建议

  • 在Fragment中打印response.body()的内容,确认解析后的数据是否正确
  • 添加Retrofit日志拦截器,查看实际请求的返回内容,验证是否与预期一致

内容的提问来源于stack exchange,提问作者Vlad Tkachev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 16:30:11