Android应用无法读取服务器JSON文件问题求助
无法读取Dreamhost服务器JSON文件的问题排查与解决
我的应用距离发布到Android Play Store只剩最后一步,但遇到了关键问题:用Kotlin开发的Android应用无法读取Dreamhost服务器上的JSON文件,文件地址为https://apis.androidparadise.site/contacts/Contacts.json。当前主Activity中when语句的event变量始终返回ContactsViewModel.RecipientLoadingEvent.Failure,仅显示失败提示Toast,且无法获取ViewModel返回的错误信息,推测JSON读取/上传逻辑存在问题,需要排查解决。
现有代码实现
数据类定义
data class ContactResponseItem( val comment: String?, @SerializedName("data") val dataList: List<Data?>, val database: String?, val name: String?, val type: String?, val version: String? )
API接口
interface ContactApi { @GET("/contacts/Contacts.json") suspend fun getAllContacts() : Response<List<ContactResponseItem>> @POST("/contacts/new") suspend fun addContact(@Body contact:ContactResponseItem): Response<ContactResponseItem> }
仓库接口与实现类
interface ContactsRepository { suspend fun getAllContacts(): Resource<List<ContactResponseItem>> suspend fun addContact(contact: ContactResponseItem): Resource<ContactResponseItem> }
class ContactsRepositoryImpl @Inject constructor(private val api: ContactApi): ContactsRepository { override suspend fun getAllContacts(): Resource<List<ContactResponseItem>> { return try { val response = api.getAllContacts() val result = response.body() if (response.isSuccessful && result != null) { Resource.Success(result) } else { Resource.Error("Response was not successful") } } catch (e: Exception) { Resource.Error(e.message ?: "Failed to retrieve contacts") } } override suspend fun addContact(contact: ContactResponseItem): Resource<ContactResponseItem> { return try { val response = api.addContact(contact) val result = response.body() Log.d("ContactsRepositoryImpl", response.isSuccessful.toString()) Log.d("ContactRepositoryImpl", (result != null).toString()) if (response.isSuccessful && result != null) { Resource.Success(result) } else { Resource.Error("Response was not successful") } } catch (e: Exception) { Resource.Error(e.message ?: "Failed to retrieve contacts") } } }
ViewModel中的方法
fun getContactEmails(): MutableList<String> { viewModelScope.launch(dispatchers!!.io) { when (val loadResponse = contactsRepo.getAllContacts()) { is Resource.Success -> { for(i in 0..loadResponse.data?.size!!){ if (loadResponse.data[i].name == "data") { val dataLoad = loadResponse.data[i].dataList[0] dataLoad?.let { emailList.add(it.email) } } } _recipientload.value = RecipientLoadingEvent.Success("The first email found: ${emailList[0]}") } is Resource.Error<*> -> RecipientLoadingEvent.Failure("Couldn't load client's emails") else -> _recipientload.value = RecipientLoadingEvent.Failure(loadResponse.errorMsg.toString()) } } return emailList }
主Activity中的代码
contactsVM.recipientLoad.collect { event -> when (event) { is ContactsViewModel.RecipientLoadingEvent.Success -> { Toast.makeText(this@CreateNewUserRegistration, contactsVM.getContactEmails()[0], Toast.LENGTH_SHORT).show() } is ContactsViewModel.RecipientLoadingEvent.Failure -> { Toast.makeText( this@CreateNewUserRegistration, "Failed to retrieve email", Toast.LENGTH_LONG ).show() Toast.makeText( this@CreateNewUserRegistration, "Failed to send data to server", Toast.LENGTH_SHORT ).show() } else -> {} } }
问题排查点
- API接口与JSON结构不匹配:若服务器返回的是单个
ContactResponseItem对象,但接口定义为返回List<ContactResponseItem>,会直接导致解析失败。 - ViewModel循环越界:
for(i in 0..loadResponse.data?.size!!)使用闭区间,会触发IndexOutOfBoundsException,因为列表索引是从0到size-1。 - 错误事件未发送:ViewModel中
Resource.Error分支仅创建了Failure事件,但未赋值给_recipientload.value,Activity无法收到错误信息。 - Activity重复发起请求:Success分支再次调用
getContactEmails(),会重新触发网络请求,且此时emailList可能未完成填充,导致异常。 - 仓库层错误信息缺失:仅返回固定文本,未提取服务器返回的具体错误内容,无法定位问题根源。
解决方案
1. 修正API接口与JSON结构匹配
先确认服务器返回的JSON结构:如果是单个对象而非列表,修改API接口:
interface ContactApi { @GET("/contacts/Contacts.json") suspend fun getAllContacts() : Response<ContactResponseItem> // 改为单个对象 @POST("/contacts/new") suspend fun addContact(@Body contact:ContactResponseItem): Response<ContactResponseItem> }
同步修改仓库和ViewModel的泛型类型,比如Resource<ContactResponseItem>,后续逻辑也对应调整为处理单个对象。
2. 修复ViewModel循环与事件发送
fun getContactEmails(): MutableList<String> { viewModelScope.launch(dispatchers!!.io) { when (val loadResponse = contactsRepo.getAllContacts()) { is Resource.Success -> { val responseData = loadResponse.data ?: run { _recipientload.value = RecipientLoadingEvent.Failure("数据为空") return@launch } // 修正循环逻辑,处理单个对象 if (responseData.name == "data") { responseData.dataList.forEach { data -> data?.email?.let { emailList.add(it) } } } // 确保有数据再发送成功事件 if (emailList.isNotEmpty()) { _recipientload.value = RecipientLoadingEvent.Success("找到第一个邮箱: ${emailList[0]}") } else { _recipientload.value = RecipientLoadingEvent.Failure("未找到邮箱数据") } } is Resource.Error<*> -> { // 正确发送错误事件 _recipientload.value = RecipientLoadingEvent.Failure(loadResponse.errorMsg ?: "加载联系人失败") } else -> { _recipientload.value = RecipientLoadingEvent.Failure("未知错误") } } } return emailList }
3. 修复Activity重复调用问题
contactsVM.recipientLoad.collect { event -> when (event) { is ContactsViewModel.RecipientLoadingEvent.Success -> { // 直接使用事件携带的消息,避免重复请求 Toast.makeText(this@CreateNewUserRegistration, event.message, Toast.LENGTH_SHORT).show() } is ContactsViewModel.RecipientLoadingEvent.Failure -> { // 显示具体错误信息 Toast.makeText( this@CreateNewUserRegistration, event.message, Toast.LENGTH_LONG ).show() } else -> {} } }
4. 优化仓库层错误处理
override suspend fun getAllContacts(): Resource<ContactResponseItem> { return try { val response = api.getAllContacts() val result = response.body() if (response.isSuccessful && result != null) { Resource.Success(result) } else { // 提取服务器返回的错误信息 val errorMsg = response.errorBody()?.string() ?: "请求失败" Resource.Error(errorMsg) } } catch (e: Exception) { Resource.Error(e.message ?: "网络请求异常") } }
额外建议
- 确认
AndroidManifest.xml中添加了网络权限:<uses-permission android:name="android.permission.INTERNET" /> - 给OkHttp添加日志拦截器,查看请求/响应的详细内容,快速定位问题
- 检查服务器CORS配置,确保允许应用域名访问接口
内容的提问来源于stack exchange,提问作者IrfanZ
相关产品推荐
相关产品推荐

