Android Kotlin ViewModel 在首次启动片段/活动时未加载/显示内容

问题描述 投票:0回答:1

我有一个 Android Kotlin MVVM 项目,首次启动 Fragment 时未加载 ViewModel。但如果我刷新它加载数据并显示在 UI 中

HomeFragment.kt UI

class HomeFragment : Fragment() {
    private val publicationViewModel: ListViewModel by viewModels()
    private lateinit var magazineList: RecyclerView
    private lateinit var paperList: RecyclerView
    private var magazineListAdapter = PaperAdapter(arrayListOf())
    private var paperListAdapter = PaperAdapter(arrayListOf())
    private lateinit var paperSkeletonScreen: LinearLayout
    private lateinit var magazineSkeletonScreen: LinearLayout
    private var pullToRefresh: SwipeRefreshLayout? = null
    var page: Int = 1

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        val view: View = LayoutInflater.from(container!!.context)
            .inflate(R.layout.fragment_main_home, container, false)
        findAll()
        paperList = view.findViewById(R.id.today_newspaper_list)
        paperList.layoutManager =
            LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false).apply {
                paperList.layoutManager
            }
        paperList.apply {
            adapter = paperListAdapter
        }
        paperSkeletonScreen = view.findViewById(R.id.newspaper_loader)

        magazineList = view.findViewById(R.id.magazine_list)
        magazineList.layoutManager =
            LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false).apply {
                magazineList.layoutManager
            }
        magazineList.apply {
            adapter = magazineListAdapter
        }
        magazineSkeletonScreen = view.findViewById(R.id.magazine_loader)

        pullToRefresh = view.findViewById(R.id.refresh_page) as SwipeRefreshLayout
        pullToRefresh?.setOnRefreshListener {
            TrackerService.trackUserEvent(requireActivity(), "refresh_home")
            refreshPage()
        }
        observeViewModelPublications()
        return view
    }

    private fun observeViewModelPublications() {
        try {
            activity?.let { it ->
                publicationViewModel.apply {
                    todayPapers.observe(it) { pubs ->
                        pubs?.let {
                            paperListAdapter.getPapers(it)
                            paperSkeletonScreen.visibility = View.GONE
                        }
                    }
                }

                publicationViewModel.apply {
                    magazine.observe(it) { pubs ->
                        pubs?.let { magazine ->
                            magazineListAdapter.getPapers(magazine)
                            magazineSkeletonScreen.visibility = View.GONE
                        }
                    }
                }

                publicationViewModel.viewModelLoadError.observe(it) { error ->
                    if (!error.isNullOrEmpty()) {
                        ToastHelper.showToast(requireActivity(), error, 1)
                    }
                }
            }
        } catch (_: Exception) {
            ToastHelper.showToast(
                requireActivity(), requireActivity().getString(R.string.something_wrong), 1
            )
        }
    }


    private fun refreshPage() {
        try {
            pullToRefresh?.isRefreshing = false
            arrayOf(
                paperSkeletonScreen, magazineSkeletonScreen
            ).forEach { it.visibility = View.VISIBLE }
            findAll()
            Handler(Looper.getMainLooper()).postDelayed({
                observeViewModelPublications()
            }, 5000)
        } catch (_: Exception) {
            ToastHelper.showToast(requireActivity(), getString(R.string.something_wrong), 1)
        }
    }

    private fun findAll() {
        publicationViewModel.findPublications("Token", "KE", 1)
    }


    companion object {
        fun newInstance(): MainHomeFragment {
            return MainHomeFragment()
        }
    }
}

与ApiService交互进行api调用并更新UI ListViewModel.kt


class ListViewModel : ViewModel() {
    private val userData = ApiClient.ApiService() 
    private var job: Job? = null

    // Publications
    val magazine = MutableLiveData<PublicationModal>()
    val todayPapers = MutableLiveData<List<PaperModal>>() 
    // Error Handling
    val viewModelLoadError = MutableLiveData<String?>()
    val loading = MutableLiveData<Boolean>()
 

    fun findPublications(bearer: String?, country: String?, page: Int?) {
        fetchTodayPapers(country)
        fetchMagazine(country)
    }

    private fun fetchTodayPapers(country: String?) {
        viewModelScope.launch {
            when (val result =
                RequestService().viewModelFetch(loading) { userData.findTodayPapers(country) }) {
                is RequestService.Result.Success -> {
                    todayPapers.value = result.data?.data
                    viewModelLoadError.value = null
                }

                is RequestService.Result.Error -> onError(result.message)
            }
        }
    }

    private fun fetchMagazine(country: String?) {
        viewModelScope.launch {
            when (val result = RequestService().viewModelFetch(loading) {
                userData.getTodayMagazine(
                    "2",
                    country
                )
            }) {
                is RequestService.Result.Success -> {
                    magazine.value = result.data?.data
                    viewModelLoadError.value = null
                }

                is RequestService.Result.Error -> onError(result.message)
            }
        }
    }
 
    private fun onError(
        message: String,
    ) {
        viewModelLoadError.value = message
        loading.value = false
    }

    override fun onCleared() {
        super.onCleared()
        job?.cancel()
    }

}

用于进行 api 调用的扩展服务会折叠其中的所有内容以删除重复的代码 此外,

 val response = fetchDataFunction()
在开始时返回 null,但当您刷新它时返回数据。 请求服务.kt


class RequestService {
 
    suspend fun <T> viewModelFetch(
        loadingLiveData: MutableLiveData<Boolean>,
        fetchDataFunction: suspend () -> Response<T>,
    ): Result<T> {
        return try {
            loadingLiveData.postValue(true)
            val response = fetchDataFunction()
            if (response.isSuccessful) {
                loadingLiveData.postValue(false)
                return Result.Success(response.body())
            } else {
                val responseBody = response.errorBody()?.toPrettyJson()
                if (responseBody == null) {
                    loadingLiveData.postValue(true)
                    Result.Success(response.body())
                } else {
                    val errorMessage =
                        responseBody.optString("message")
                            ?: responseBody.optString("error")
                    loadingLiveData.postValue(false)
                    return Result.Error(errorMessage)
                }
            }
        } catch (e: SocketTimeoutException) {
            loadingLiveData.postValue(false)
            return Result.Error("Request is taking too long to complete, try again later")
        } catch (e: IOException) {
            loadingLiveData.postValue(false)
            return Result.Error("Network error, check your internet connection")
        } catch (e: Exception) {
            loadingLiveData.postValue(false)
            return Result.Error("Network error, check your internet connection")
        }
    }

    sealed class Result<out T> {
        data class Success<out T>(val data: T?) : Result<T>()
        data class Error(val message: String) : Result<Nothing>()
    }
}

ApiClient.kt 调用我的 Api 并返回数据

  @GET("today")
    suspend fun findTodayPapers(
        @Query("country") country: String?,
    ): Response<PaperData>
     

  @GET("latest")
    suspend fun getTodayMagazine(
        @Query("country") country: String?,
    ): Response<PaperData>



上面的代码有什么问题,请帮忙

android kotlin android-viewmodel android-mvvm
1个回答
0
投票

我在您的代码中看到的一个可能的问题是您正在 onCreateView() 中观察 LiveData。首次创建 Fragment 时,会调用 onCreateView(),并且在该方法完成之前 UI 尚未准备好。这也许可以解释为什么你的 UI 最初没有加载。

要解决此问题,您应该将 LiveData 观察移至 onViewCreated() 或 onActivityCreated() 而不是 onCreateView()。这些生命周期方法在视图创建后调用,通过观察其中的 LiveData,您可以确保只要 LiveData 发出数据,您的 UI 就会更新。

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    observeViewModelPublications()
}
© www.soinside.com 2019 - 2024. All rights reserved.