Android API调用始终返回空响应问题排查求助(附代码)
问题排查:API请求正常但返回空响应
调用API获取用户数据时,请求发送状态正常,但始终得到空响应,不确定是否是路径调用错误,以下是相关实现代码及截图:
数据类
data class ResponseGetDataUser( @SerializedName("address") val address: String, @SerializedName("city") val city: String, @SerializedName("created_at") val createdAt: String, @SerializedName("email") val email: String, @SerializedName("email_verified_at") val emailVerifiedAt: Any, @SerializedName("id") val id: Int, @SerializedName("image") val image: String, @SerializedName("name") val name: String, @SerializedName("phone") val phone: String, @SerializedName("updated_at") val updatedAt: String )
ApiService
@GET("user") suspend fun getDataUser(@Header("token") token: String): ResponseGetDataUser
ApiHelper
suspend fun getDataUser(token: String) = apiService.getDataUser(token)
Repository
suspend fun getDataUser(token: String) = apiHelper.getDataUser(token)
ViewModel
@HiltViewModel class AkunViewModel @Inject constructor(private val repository: Repository): ViewModel() { private var _alreadyLogin = MutableLiveData<String>() val alreadyLogin : LiveData<String> get() = _alreadyLogin private var _user = MutableLiveData<Resource<ResponseGetDataUser>>() val user : LiveData<Resource<ResponseGetDataUser>> get() = _user fun getToken(){ viewModelScope.launch { repository.getToken().collect{ _alreadyLogin.postValue(it) } } } fun getUserData(token: String){ viewModelScope.launch { _user.postValue(Resource.loading()) try { _user.postValue(Resource.success(repository.getDataUser(token))) } catch (e: Exception){ _user.postValue(Resource.error(e.localizedMessage?: "Error Occured")) } } } fun deleteToken(){ viewModelScope.launch { repository.deleteToken() } } }
Fragment类
@AndroidEntryPoint class AkunFragment : Fragment() { private var _binding: FragmentAkunBinding? = null private val binding get() = _binding!! private val viewModel: AkunViewModel by viewModels() private val bundle = Bundle() companion object { const val USER_NAME = "fullname" const val USER_EMAIL = "email" const val USER_PHONE_NUMBER = "phone_number" const val USER_ADDRESS = "address" const val USER_CITY = "city" const val USER_IMAGE = "image_url" const val USER_TOKEN = "user_token" } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentAkunBinding.inflate(inflater, container, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) val progressDialog = ProgressDialog(requireContext()) viewModel.getToken() viewModel.alreadyLogin.observe(viewLifecycleOwner) { if (it == DEFAULT_TOKEN) { AlertDialog.Builder(requireContext()) .setTitle("Pesan") .setMessage("Anda Belom Masuk") .setPositiveButton("Login") { dialogP, _ -> findNavController().navigate(R.id.action_akunFragment_to_loginCompose) dialogP.dismiss() } .setNegativeButton("Cancel") { dialogN, _ -> findNavController().navigate(R.id.action_akunFragment_to_homeFragment) dialogN.dismiss() } .setCancelable(false) .show() viewModel.alreadyLogin.removeObservers(viewLifecycleOwner) } else { bundle.putString(USER_TOKEN, it) viewModel.getUserData(it) } } viewModel.user.observe(viewLifecycleOwner) { when (it.status) { SUCCESS-> { progressDialog.dismiss() if (it.data != null) { bundle.putString(USER_NAME, it.data.name) bundle.putString(USER_EMAIL, it.data.email) bundle.putString(USER_CITY, it.data.city) bundle.putString(USER_ADDRESS, it.data.address) bundle.putString(USER_PHONE_NUMBER, it.data.phone) binding.tvNama.text = it.data.name binding.tvNomor.text = it.data.phone binding.tvEmail.text = it.data.email if (it.data.image != null) { Glide.with(requireContext()) .load(it.data.image) .placeholder(R.drawable.image_profile) .transform(CenterCrop(), RoundedCorners(50)) .into(binding.ivProfile) bundle.putString(USER_IMAGE, it.data.image) } } } ERROR -> { Toast.makeText(requireContext(), it.message, Toast.LENGTH_SHORT) .show() progressDialog.dismiss() } LOADING -> { progressDialog.setMessage("Please Wait...") progressDialog.show() } } } binding.listUbahAkun.setOnClickListener { findNavController().navigate(R.id.action_akunFragment_to_editAkunFragment, bundle) } binding.listPengaturanAkun.setOnClickListener { val dialogBinding = MenuPengaturanAkunBinding.inflate(LayoutInflater.from(requireContext())) val dialogBuilder = androidx.appcompat.app.AlertDialog.Builder(requireContext()) dialogBuilder.setView(dialogBinding.root) val dialog = dialogBuilder.create() dialog.setCancelable(true) dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT)) dialogBinding.listChangePassword.setOnClickListener { findNavController().navigate(R.id.action_akunFragment_to_changePasswordFragment, bundle) dialog.dismiss() } dialogBinding.listClose.setOnClickListener { dialog.dismiss() } dialog.show() } binding.listRiwayatPenawaran.setOnClickListener { findNavController().navigate(R.id.action_akunFragment_to_riwayatPenawaranFragment, bundle) } binding.listWishlist.setOnClickListener{ findNavController().navigate(R.id.action_akunFragment_to_wishlistFragment) } binding.listKeluar.setOnClickListener { AlertDialog .Builder(requireContext()) .setTitle("Konfirmasi Keluar") .setMessage("Yakin ingin keluar?") .setPositiveButton("Iya") { dialogPositive, _ -> viewModel.deleteToken() Toast .makeText( requireContext(), "Logout Success", Toast.LENGTH_SHORT ) .show() findNavController().navigate(R.id.action_akunFragment_to_homeFragment) dialogPositive.dismiss() } .setNegativeButton("Tidak") { dialogNegative, _ -> dialogNegative.dismiss() } .setCancelable(false) .show() } } }
相关截图



排查建议
- 核对API路径:确认
@GET("user")是否与后端实际接口路径完全一致,比如是否缺少前缀(如/api/user),或是需要携带用户ID的动态路径(如/user/{id})。 - 匹配响应结构:检查后端返回的JSON是否有外层包裹(例如
{"data": {...}}),若存在则需调整数据类结构,新增一层嵌套类来接收外层数据:
同时将ApiService的返回类型改为data class UserApiResponse( @SerializedName("data") val userData: ResponseGetDataUser? )UserApiResponse。 - 验证Token有效性:用Postman等工具携带相同Token调用接口,确认后端是否返回有效数据,排除Token权限不足或过期的问题。
- 调整数据类可空性:若后端部分字段可能返回
null,需将数据类中对应字段改为可空类型(如String?),避免Gson解析失败导致数据为空。 - 添加Retrofit日志拦截器:打印完整请求响应日志,明确后端实际返回内容,方便定位问题:
在Retrofit构建时使用该OkHttpClient即可查看详细日志。val loggingInterceptor = HttpLoggingInterceptor().apply { level = HttpLoggingInterceptor.Level.BODY } val okHttpClient = OkHttpClient.Builder() .addInterceptor(loggingInterceptor) .build()
内容的提问来源于stack exchange,提问作者FarhanF Arkaan
相关产品推荐
相关产品推荐

