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

如何在ViewModel中处理异步请求的Result<List<Author>>结果并在Fragment展示

处理Result<List>的ViewModel实现方案

翻译后的仓库代码示例

// 异步获取作者列表,返回Result类型,可能成功或失败
suspend fun getAuthors(): Result<List<Author>> {
    randomDelay() // 模拟网络/本地加载延迟
    return randomResult(
        listOf(
            Author(
                authorIds[0],
                "作者一号"
            ),
            Author(
                authorIds[1],
                "作者二号"
            ),
        )
    )
}
// 模拟随机延迟,时长在100ms到1000ms之间
private suspend fun randomDelay() {
    delay((100L..1000L).random())
}

// 生成随机结果:5%概率返回失败,95%概率返回成功数据
private fun <T> randomResult(data: T): Result<T> =
    if ((0..100).random() < 5) {
        Result.failure(RuntimeException())
    } else {
        Result.success(data)
    }

核心实现思路

通过密封类定义UI状态,ViewModel用协程调用仓库方法并处理Result的成功/失败分支,将状态通过StateFlow暴露给Fragment,Fragment根据状态更新ListView和错误文本的显示,重点关注失败场景的错误提示与UI切换。

步骤1:定义UI状态密封类

用密封类明确区分加载、成功、失败三种状态,确保UI状态的完整性:

sealed class AuthorsUiState {
    object Loading : AuthorsUiState() // 加载中
    data class Success(val authors: List<Author>) : AuthorsUiState() // 加载成功
    data class Error(val message: String) : AuthorsUiState() // 加载失败
}

步骤2:ViewModel处理Result并更新状态

使用viewModelScope启动协程,通过Result.fold()方法分别处理成功和失败分支,将结果转化为UI状态:

class AuthorsViewModel(private val authorRepository: AuthorRepository) : ViewModel() {
    // 内部可变状态,对外暴露不可变的StateFlow
    private val _uiState = MutableStateFlow<AuthorsUiState>(AuthorsUiState.Loading)
    val uiState: StateFlow<AuthorsUiState> = _uiState.asStateFlow()

    init {
        loadAuthors() // 初始化时自动加载数据
    }

    private fun loadAuthors() {
        viewModelScope.launch {
            val result = authorRepository.getAuthors()
            result.fold(
                onSuccess = { authors ->
                    // 成功时更新为Success状态,传递作者列表
                    _uiState.value = AuthorsUiState.Success(authors)
                },
                onFailure = { exception ->
                    // 失败时根据异常类型生成友好提示
                    val errorMsg = when (exception) {
                        is RuntimeException -> "加载作者列表失败,请稍后重试"
                        else -> "未知错误:${exception.message ?: "无详细信息"}"
                    }
                    _uiState.value = AuthorsUiState.Error(errorMsg)
                }
            )
        }
    }

    // 可选:提供重试方法,供Fragment的重试按钮调用
    fun retryLoadAuthors() {
        _uiState.value = AuthorsUiState.Loading
        loadAuthors()
    }
}

步骤3:Fragment观察状态并更新UI

在Fragment中使用lifecycleScope和repeatOnLifecycle安全观察状态,根据不同状态控制ListView和错误TextView的显示:

class AuthorsFragment : Fragment() {
    private lateinit var viewModel: AuthorsViewModel
    private lateinit var authorsListView: ListView
    private lateinit var errorTextView: TextView
    private lateinit var authorsAdapter: AuthorsAdapter

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_authors, container, false)
        authorsListView = view.findViewById(R.id.authors_list)
        errorTextView = view.findViewById(R.id.error_text)
        // 初始化适配器,默认空列表
        authorsAdapter = AuthorsAdapter(requireContext(), emptyList())
        authorsListView.adapter = authorsAdapter
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel = ViewModelProvider(this)[AuthorsViewModel::class.java]

        lifecycleScope.launch {
            // 在STARTED状态下收集状态,避免内存泄漏
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                viewModel.uiState.collect { state ->
                    when (state) {
                        AuthorsUiState.Loading -> {
                            // 加载中:隐藏列表和错误文本,可显示进度条
                            authorsListView.visibility = View.GONE
                            errorTextView.visibility = View.GONE
                            // progressBar.visibility = View.VISIBLE
                        }
                        is AuthorsUiState.Success -> {
                            // 成功:显示列表,隐藏错误文本和进度条
                            authorsListView.visibility = View.VISIBLE
                            errorTextView.visibility = View.GONE
                            // progressBar.visibility = View.GONE
                            authorsAdapter.updateData(state.authors)
                        }
                        is AuthorsUiState.Error -> {
                            // 失败:隐藏列表和进度条,显示错误文本
                            authorsListView.visibility = View.GONE
                            errorTextView.visibility = View.VISIBLE
                            // progressBar.visibility = View.GONE
                            errorTextView.text = state.message
                        }
                    }
                }
            }
        }

        // 可选:给错误文本添加点击重试
        errorTextView.setOnClickListener {
            viewModel.retryLoadAuthors()
        }
    }
}

步骤4:ListView适配器实现

简单的BaseAdapter,支持更新数据:

class AuthorsAdapter(
    private val context: Context,
    private var authors: List<Author>
) : BaseAdapter() {
    private val inflater = LayoutInflater.from(context)

    // 更新数据并通知列表刷新
    fun updateData(newAuthors: List<Author>) {
        authors = newAuthors
        notifyDataSetChanged()
    }

    override fun getCount(): Int = authors.size

    override fun getItem(position: Int): Author = authors[position]

    override fun getItemId(position: Int): Long = authors[position].authorId

    override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View {
        val view = convertView ?: inflater.inflate(android.R.layout.simple_list_item_1, parent, false)
        val textView = view.findViewById<TextView>(android.R.id.text1)
        textView.text = authors[position].name
        return view
    }
}

失败场景处理要点

  1. 异常分类处理:根据不同异常类型返回针对性的错误提示,比如网络异常提示“网络连接失败”,本地数据异常提示“本地数据加载失败”,提升用户体验。
  2. 重试机制:提供重试入口(如错误文本点击重试),让用户可以主动触发重新加载。
  3. 状态可视化:加载中显示进度条,失败时明确展示错误信息,避免用户困惑。
  4. 错误信息本地化:将错误提示字符串放入strings.xml,方便多语言适配。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 02:50:22