Android Retrofit API调用无响应无报错问题排查求助
问题排查:Retrofit API调用未触发(ViewModel函数未执行)
核心问题
ViewModel中的fetchingBanner()函数完全未执行,导致Retrofit API调用未触发,根源在于UI层从未向ViewModel发送触发请求的Intent,同时存在Intent设计冗余、状态处理不完善等问题。
具体排查与修复步骤
1. 修复HomeIntent的冗余设计
原LoadBanners Intent携带了不必要的参数(要获取的数据列表),这会造成逻辑混淆,修改为无参数的事件:
sealed class HomeIntent { object LoadBanners : HomeIntent() // 仅作为触发加载的信号,无需传参 }
2. 完善ViewModel的逻辑处理
- 匹配修改后的无参数Intent
- 添加加载状态通知
- 安全处理响应体(避免空指针)
- 补充异常捕获与详细错误信息
@HiltViewModel class HomeViewModel @Inject constructor(private val repository: HomeRepository) : ViewModel() { val intentChannel = Channel<HomeIntent>() private val _state = MutableStateFlow<HomeState>(HomeState.Idle) val state: StateFlow<HomeState> get() = _state init { handleIntents() } private fun handleIntents() = viewModelScope.launch { intentChannel.consumeAsFlow().collect { intent -> when (intent) { HomeIntent.LoadBanners -> fetchingBanner() // 匹配无参数Intent } } } private suspend fun fetchingBanner() { _state.emit(HomeState.LoadingBanner) // 发送加载中状态,更新UI try { val response = repository.getBannerList() when (response.code()) { in 200..202 -> { val bannerList = response.body() ?: mutableListOf() if (bannerList.isEmpty()) { _state.emit(HomeState.Empty) } else { _state.emit(HomeState.BannerListItem(bannerList)) } } in 400..499 -> { _state.emit(HomeState.Error("客户端错误:${response.code()}")) } in 500..599 -> { _state.emit(HomeState.Error("服务端错误:${response.code()}")) } else -> { _state.emit(HomeState.Error("未知错误:${response.code()}")) } } } catch (e: Exception) { // 捕获网络异常、解析异常等 _state.emit(HomeState.Error("请求失败:${e.message ?: "未知错误"}")) } } }
3. 在Fragment中发送Intent并完善UI逻辑
原Fragment仅观察状态但未发送触发请求的Intent,添加Intent发送逻辑,并完善状态对应的UI更新:
@AndroidEntryPoint class HomeFragment : Fragment() { private var _binding: FragmentHomeBinding? = null private val binding get() = _binding!! @Inject lateinit var bannerAdapter: AdapterBanner private val viewModel: HomeViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentHomeBinding.inflate(layoutInflater) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // 初始化RecyclerView binding.bannerRecyclerView.apply { adapter = bannerAdapter layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false) } // 发送加载Banner的Intent,触发API调用 viewModel.viewModelScope.launch { viewModel.intentChannel.send(HomeIntent.LoadBanners) } // 观察ViewModel状态,更新UI viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.state.collect { state -> when (state) { HomeState.Idle -> {} HomeState.LoadingBanner -> { binding.loadingProgress.visibility = View.VISIBLE binding.emptyState.visibility = View.GONE } HomeState.LoadingProfile -> {} is HomeState.BannerListItem -> { binding.loadingProgress.visibility = View.GONE bannerAdapter.submitList(state.banner) } HomeState.Empty -> { binding.loadingProgress.visibility = View.GONE binding.emptyState.visibility = View.VISIBLE } is HomeState.Error -> { binding.loadingProgress.visibility = View.GONE Toast.makeText(requireContext(), state.error, Toast.LENGTH_SHORT).show() } } } } } } override fun onDestroyView() { super.onDestroyView() _binding = null } }
额外检查点
- 确认Retrofit实例配置正确:包含正确的
baseUrl,添加了GsonConverterFactory(或其他序列化转换器) - 确认Hilt配置完整:Application类添加
@HiltAndroidApp注解,ApiServices通过@Provides正确注入 - 确认AndroidManifest.xml配置:
<uses-permission android:name="android.permission.INTERNET" /> <application ... android:usesCleartextTraffic="true"> ... </application>
内容的提问来源于stack exchange,提问作者said sobhani
相关产品推荐
相关产品推荐

