Android Kotlin ViewModel 错误处理:最佳 Toast 提示方案详解

在 Android Kotlin 的 ViewModel 中显示 Toast 提示需要特殊处理,因为 ViewModel 不应该直接持有 Context(避免内存泄漏),且 UI 操作(如 Toast)必须在主线程执行。以下是几种标准解决方案:


方案1:通过 LiveData 传递消息(推荐)

kotlin 复制代码
class MyViewModel : ViewModel() {
    // 定义消息事件(使用SingleLiveEvent避免重复触发)
    private val _toastMessage = MutableLiveData<String>()
    val toastMessage: LiveData<String> get() = _toastMessage

    fun fetchData() {
        viewModelScope.launch {
            try {
                // 模拟网络请求
                val result = apiService.getData()
                // ...处理数据
            } catch (e: Exception) {
                _toastMessage.postValue("加载失败: ${e.message}")
            }
        }
    }
}

// Activity/Fragment 中观察
class MyActivity : AppCompatActivity() {
    private val viewModel by viewModels<MyViewModel>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 观察Toast消息
        viewModel.toastMessage.observe(this) { message ->
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
        }
    }
}

方案2:使用 Event Wrapper(避免重复触发)

kotlin 复制代码
// 定义事件包装类
class Event<T>(private val content: T) {
    private var hasBeenHandled = false

    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) null else {
            hasBeenHandled = true
            content
        }
    }
}

// ViewModel 中使用
class MyViewModel : ViewModel() {
    private val _toastEvent = MutableLiveData<Event<String>>()
    val toastEvent: LiveData<Event<String>> get() = _toastEvent

    fun fetchData() {
        viewModelScope.launch {
            try {
                // ...业务逻辑
            } catch (e: Exception) {
                _toastEvent.postValue(Event("错误: ${e.message}"))
            }
        }
    }
}

// Activity/Fragment 中观察
viewModel.toastEvent.observe(this) { event ->
    event.getContentIfNotHandled()?.let { message ->
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
    }
}

方案3:通过 Application Context(谨慎使用)

kotlin 复制代码
class MyViewModel(application: Application) : AndroidViewModel(application) {
    private val appContext = application.applicationContext

    fun showError() {
        // 注意:必须在主线程调用
        Handler(Looper.getMainLooper()).post {
            Toast.makeText(appContext, "出错啦!", Toast.LENGTH_SHORT).show()
        }
    }
}

⚠️ 此方案虽简便,但过度使用可能导致Toast与当前界面上下文不匹配


方案4:通过接口回调(适合复杂场景)

kotlin 复制代码
interface ToastListener {
    fun showToast(message: String)
}

class MyViewModel(private val toastListener: ToastListener) : ViewModel() {
    fun fetchData() {
        viewModelScope.launch {
            try {
                // ...业务逻辑
            } catch (e: Exception) {
                toastListener.showToast(e.message ?: "未知错误")
            }
        }
    }
}

// Activity中实现接口
class MyActivity : AppCompatActivity(), ToastListener {
    private val viewModel by viewModels<MyViewModel> { 
        MyViewModelFactory(this) 
    }

    override fun showToast(message: String) {
        runOnUiThread {
            Toast.makeText(this, message, Toast.LENGTH_SHORT).show()
        }
    }
}

// 自定义Factory
class MyViewModelFactory(private val listener: ToastListener) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MyViewModel(listener) as T
    }
}

最佳实践总结

  1. 推荐方案

    • 简单场景:方案1(LiveData)
    • 需要防重复:方案2(Event Wrapper)
  2. 线程安全

    • 确保Toast在主线程显示(postValuerunOnUiThread
  3. 架构原则

    • ViewModel 不应直接持有 Context
    • 通过观察者模式实现解耦
  4. 错误处理

    kotlin 复制代码
    catch (e: IOException) {
        _toastMessage.postValue("网络异常")
    } catch (e: IllegalStateException) {
        _toastMessage.postValue("数据格式错误")
    }

根据您的架构复杂度和需求选择合适方案即可。

相关推荐
用户693717500138418 小时前
🚀 Jetpack MVI 实战全解析:一次彻底搞懂 MVI 架构,让状态管理像点奶茶一样丝滑!
android·android jetpack
2501_9159184119 小时前
iOS 上架应用市场全流程指南,App Store 审核机制、证书管理与跨平台免 Mac 上传发布方案(含开心上架实战)
android·macos·ios·小程序·uni-app·cocoa·iphone
峥嵘life20 小时前
Android EDLA 打开5G热点失败分析解决2
android·5g
消失的旧时光-194321 小时前
webkitx(Android WebView 最佳实践库)--> 上
android·webview
安卓兼职framework应用工程师1 天前
android 15.0 app应用安装黑名单
android·pms·install·rom·安装黑名单
泷羽Sec-静安1 天前
Less-7 GET-Dump into outfile-String
android·前端·网络·sql·安全·web安全
花花鱼1 天前
html5与android之间相互调用
android
aqi001 天前
FFmpeg开发笔记(八十八)基于Compose的国产电视直播开源框架MyTV
android·ffmpeg·音视频·直播·流媒体
●VON1 天前
双非大学生自学鸿蒙5.0零基础入门到项目实战 -《基础篇》
android·华为·harmonyos·鸿蒙
urkay-1 天前
Android Cursor AI代码编辑器
android·人工智能·编辑器·iphone·androidx