如果没有无限循环,我如何检查JSON数据是否为null



我有一个视图模型和数据类,可以获取火星照片的NASA api。用户应该显示来自随机查询日期的图像。我总是需要返回一个图像url(照片类中的imgSrc(。如果找不到url(imgSrc(,则刷新数据,直到找到一个并显示它。如果用户选择滑动刷新,则此逻辑需要在应用程序启动后以及滑动freshlayout后返回imgSrc。我在这个问题上已经坚持了一个星期,没有下定决心。处理这个问题的最佳方法是什么?即使我必须重构我的代码,我也希望被指向正确的方向。

这是github上的实际项目。

我想获取的JSON

JSON未返回imgSrc

视图模型

import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.dev20.themarsroll.models.MarsPhotos
import com.dev20.themarsroll.models.Photo
import com.dev20.themarsroll.repository.MarsPhotoRepository
import com.dev20.themarsroll.util.Resource
import kotlinx.coroutines.launch
import retrofit2.Response
class MarsPhotoViewModel(
private val marsPhotoRepository: MarsPhotoRepository
): ViewModel() {
val marsPhotos: MutableLiveData<Resource<MarsPhotos>> = MutableLiveData()
init {
getRandomPhotos()
}
fun getCuriosityPhotos(solQuery: Int, roverQuery: Int, camera: String) = viewModelScope.launch {
marsPhotos.postValue(Resource.Loading())
val response = marsPhotoRepository.getCuriosityPhotos(solQuery, roverQuery, camera)
marsPhotos.postValue(handlePhotosResponse(response))
}
private fun handlePhotosResponse(response: Response<MarsPhotos> ) : Resource<MarsPhotos> {
if(response.isSuccessful) {
response.body()?.let { resultResponse ->
return Resource.Success(resultResponse)
}
}
return Resource.Error(response.message())
}
fun getRandomPhotos() {
getCuriosityPhotos((1..2878).random(), 5, "NAVCAM")
}
fun savePhoto(photo: Photo) = viewModelScope.launch {
marsPhotoRepository.upsert(photo)
}
fun getSavedPhotos() = marsPhotoRepository.getSavedPhotos()
fun deletePhoto(photo: Photo) = viewModelScope.launch {
marsPhotoRepository.deletePhoto(photo)
}
}

CuriosityFragment


import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.View
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.dev20.themarsroll.R
import com.dev20.themarsroll.adapters.MarsPhotoAdapter
import com.dev20.themarsroll.util.Resource
import com.dev20.ui.MarsActivity
import com.dev20.ui.MarsPhotoViewModel
import kotlinx.android.synthetic.main.fragment_curiosity.*

class CuriosityFragment : Fragment(R.layout.fragment_curiosity) {
lateinit var viewModel: MarsPhotoViewModel
lateinit var marsPhotoAdapter: MarsPhotoAdapter

val TAG = "CuriosityFragment"
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = (activity as MarsActivity).viewModel
setupRecyclerView()
swipeLayout.setOnRefreshListener {
viewModel.getRandomPhotos()
swipeLayout.isRefreshing = false
}
marsPhotoAdapter.setOnItemClickListener {
val bundle = Bundle().apply {
putSerializable("photo", it)
}
findNavController().navigate(
R.id.action_curiosityFragment_to_cameraFragment,
bundle
)
}
viewModel.marsPhotos.observe(viewLifecycleOwner, { response ->
when(response) {
is Resource.Success -> {
hideProgressBar()
response.data?.let { curiosityResponse ->
marsPhotoAdapter.differ.submitList(curiosityResponse.photos)
}
}
is Resource.Error -> {
hideProgressBar()
response.message?.let { message ->
Log.e(TAG, "An Error occurred: $message")
}
}
is Resource.Loading -> {
showProgressBar()
}
}
})
}
private fun hideProgressBar() {
curiosityPaginationProgressBar.visibility = View.INVISIBLE
}
private fun showProgressBar() {
curiosityPaginationProgressBar.visibility = View.VISIBLE
}
private fun setupRecyclerView() {
marsPhotoAdapter = MarsPhotoAdapter()
rvCuriosityPhotos.apply {
adapter = marsPhotoAdapter
layoutManager = LinearLayoutManager(activity)
}
}
}

MarsShoto数据类

data class MarsPhotos(
val photos: MutableList<Photo>,
val camera: MutableList<Camera>
)

照片数据类


import androidx.room.Entity
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.google.gson.annotations.SerializedName
import java.io.Serializable
@Entity(
tableName = "photos"
)
@TypeConverters
data class Photo(
@PrimaryKey(autoGenerate = true)
var id: Int? = null,
@SerializedName("earth_date")
val earthDate: String,
@SerializedName("img_src")
val imgSrc: String,
val sol: Int,
@SerializedName("rover_id")
val rover: Int,
) : Serializable

我能想到很多潜在的解决方案。然而,考虑到应用程序需要具有可预测和合理的用户体验,在这里我首先要找出问题所在。

  • 由于每次都请求一个随机资源,所以它总是有可能为空。因此,不能取消多次往返(但可以减少(
  • 多次HTTP往返,再加上多次返回null的不可预测性,可能会让用户体验非常沮丧

以下是处理这一问题的潜在方法(按复杂性的增加顺序(。

  1. 最简单的解决方案是在存储库级别实现逻辑,其中函数getCuriosityPhotos负责无限期地请求api资源,直到它以非空数据响应。这将解决核心问题,即用户最终会看到一些东西(但这可能需要大量时间(

(PS-您还需要将随机数生成委托为存储库可用的潜在服务。(

  1. 为了减少请求数量,从而减少用户的等待时间,您可以将请求参数和响应保存到应用内数据库中。因此,您的数据库可以作为一个单一的真相来源。因此,在提出请求之前,您可以查询数据库,以检查应用程序之前是否请求过相同的参数。如果没有,则发送请求;如果发送了,则无需再次请求,您可以使用以前的结果。如果为null,请重新生成另一个随机数,然后重试。如果它不为null,则提供数据库中的数据。(这是一个足够好的解决方案,随着越来越多的请求和响应被保存,用户等待时间将不断减少(

(注意:如果端点没有用静态数据响应,并且数据不断变化,则更喜欢使用内存中数据库,而不是像SQLite这样的持久数据库(

  1. 应用程序可以运行后台服务,该服务可以不断(通过迭代所有可能的请求参数组合(请求并将数据保存到数据库中。当用户请求随机数据时,应用程序应显示数据库中的随机数据集。如果数据库为空/不满足数据库中至少有n行的阈值,则应用程序可能会显示初始化设置UI

Pro提示:理想情况下(如果你正在构建产品/服务(,移动应用程序应该非常可预测,并且必须注意用户的时间。因此,从这些资源请求数据的任务应该是后端服务器和数据库的任务,它们操作某种服务来获取和存储数据,然后应用程序将请求该服务器在不具有任何空值的子集中获取数据。

我从解决不同粒度问题的角度回答了这个问题。如果您在技术实现部分需要帮助/建议,请告诉我,我很乐意提供帮助!

最新更新