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

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": {...}}),若存在则需调整数据类结构,新增一层嵌套类来接收外层数据:
    data class UserApiResponse(
        @SerializedName("data") val userData: ResponseGetDataUser?
    )
    
    同时将ApiService的返回类型改为UserApiResponse。
  • 验证Token有效性:用Postman等工具携带相同Token调用接口,确认后端是否返回有效数据,排除Token权限不足或过期的问题。
  • 调整数据类可空性:若后端部分字段可能返回null,需将数据类中对应字段改为可空类型(如String?),避免Gson解析失败导致数据为空。
  • 添加Retrofit日志拦截器:打印完整请求响应日志,明确后端实际返回内容,方便定位问题:
    val loggingInterceptor = HttpLoggingInterceptor().apply {
        level = HttpLoggingInterceptor.Level.BODY
    }
    val okHttpClient = OkHttpClient.Builder()
        .addInterceptor(loggingInterceptor)
        .build()
    
    在Retrofit构建时使用该OkHttpClient即可查看详细日志。

内容的提问来源于stack exchange,提问作者FarhanF Arkaan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:47:23