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

Android多层架构下如何将异常消息传递至Activity并触发事件?

嘿,这个问题在MVVM架构里太常见了——咱们肯定不能让底层的NetworkDataSource直接去调用Activity的方法,那样会把层级搞乱,还容易造成内存泄漏。下面给你几个既符合架构规范又好用的方案,按推荐程度排序:

方案1:用LiveData(最贴合MVVM的首选方案)

LiveData本来就是为ViewModel和UI层(Activity/Fragment)的生命周期安全通信设计的,刚好适合传递错误信息。核心思路是把错误信息从NetworkDataSource往上传递到Repository,再通过LiveData暴露给ViewModel,最后由Activity观察并弹出Toast。

代码示例:

1. NetworkDataSource层(定义回调传递错误)

class NetworkDataSource {
    // 定义回调接口,用来通知上层请求结果
    interface DataCallback {
        fun onSuccess(data: YourDataModel)
        fun onError(errorMsg: String)
    }

    fun fetchData(callback: DataCallback) {
        // 用IO线程做网络请求
        CoroutineScope(Dispatchers.IO).launch {
            try {
                val response = YourApiService.getInstance().getData()
                if (response.isSuccessful) {
                    callback.onSuccess(response.body()!!)
                } else {
                    // 把HTTP错误信息传给上层
                    callback.onError("请求失败:${response.message()}")
                }
            } catch (e: Exception) {
                // 捕获网络异常(比如无网、超时)
                callback.onError("网络错误:${e.message ?: "未知错误"}")
            }
        }
    }
}

2. Repository层(持有错误LiveData)

class YourRepository {
    private val networkDataSource = NetworkDataSource()
    // 私有MutableLiveData用来更新错误,对外暴露不可变的LiveData
    private val _errorLiveData = MutableLiveData<String>()
    val errorLiveData: LiveData<String> = _errorLiveData

    fun fetchData(): LiveData<YourDataModel> {
        val dataLiveData = MutableLiveData<YourDataModel>()
        networkDataSource.fetchData(object : NetworkDataSource.DataCallback {
            override fun onSuccess(data: YourDataModel) {
                // 成功时更新数据LiveData
                dataLiveData.postValue(data)
            }

            override fun onError(errorMsg: String) {
                // 错误时更新错误LiveData
                _errorLiveData.postValue(errorMsg)
            }
        })
        return dataLiveData
    }
}

3. ViewModel层(转发LiveData)

class YourViewModel(private val repository: YourRepository) : ViewModel() {
    // 直接把Repository的LiveData暴露给Activity
    val dataLiveData = repository.fetchData()
    val errorLiveData = repository.errorLiveData
}

4. Activity层(观察LiveData并显示Toast)

class YourActivity : AppCompatActivity() {
    private lateinit var viewModel: YourViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 初始化ViewModel
        viewModel = ViewModelProvider(this)[YourViewModel::class.java]

        // 观察数据更新UI
        viewModel.dataLiveData.observe(this) { data ->
            // 这里处理数据展示逻辑,比如更新RecyclerView
        }

        // 观察错误信息,弹出Toast
        viewModel.errorLiveData.observe(this) { errorMsg ->
            Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
        }
    }
}

方案2:用Kotlin Flow(现代响应式方案)

如果你的项目已经在使用Kotlin Flow,那用它来传递错误会更流畅。我们可以把请求结果封装成密封类,成功和错误都用Flow发射。

代码示例:

1. 定义结果密封类

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

2. NetworkDataSource层(返回Flow结果)

class NetworkDataSource {
    private val apiService = YourApiService.getInstance()

    suspend fun fetchData(): RequestResult<YourDataModel> {
        return try {
            val response = apiService.getData()
            if (response.isSuccessful) {
                RequestResult.Success(response.body()!!)
            } else {
                RequestResult.Error("请求失败:${response.message()}")
            }
        } catch (e: Exception) {
            RequestResult.Error("网络错误:${e.message ?: "未知错误"}")
        }
    }
}

3. Repository层(包装成Flow)

class YourRepository {
    private val networkDataSource = NetworkDataSource()

    fun fetchData(): Flow<RequestResult<YourDataModel>> = flow {
        emit(networkDataSource.fetchData())
    }.flowOn(Dispatchers.IO) // 指定IO线程执行
}

4. ViewModel层(转成LiveData给UI)

class YourViewModel(private val repository: YourRepository) : ViewModel() {
    // 把Flow转成LiveData,方便Activity观察
    val requestResult = repository.fetchData().asLiveData()
}

5. Activity层(处理结果)

viewModel.requestResult.observe(this) { result ->
    when (result) {
        is RequestResult.Success -> {
            // 展示数据
        }
        is RequestResult.Error -> {
            Toast.makeText(this, result.message, Toast.LENGTH_SHORT).show()
        }
    }
}

方案3:用接口回调(传统方案,适合小型项目)

如果不想用LiveData或Flow,也可以通过多层接口回调传递错误,但要注意内存泄漏问题——最好用弱引用或者在Activity销毁时取消回调。

代码示例:

1. 定义全局错误回调接口

interface ErrorCallback {
    fun onError(errorMsg: String)
}

2. 层层传递回调

NetworkDataSource接收回调,Repository接收回调再传给NetworkDataSource,ViewModel同理,最后Activity实现这个接口:

// NetworkDataSource
class NetworkDataSource(private val errorCallback: ErrorCallback) {
    fun fetchData() {
        CoroutineScope(Dispatchers.IO).launch {
            try {
                val response = YourApiService.getInstance().getData()
                if (!response.isSuccessful) {
                    errorCallback.onError("请求失败:${response.message()}")
                }
            } catch (e: Exception) {
                errorCallback.onError("网络错误:${e.message}")
            }
        }
    }
}

// Repository
class YourRepository(private val errorCallback: ErrorCallback) {
    private val networkDataSource = NetworkDataSource(errorCallback)
    fun fetchData() = networkDataSource.fetchData()
}

// ViewModel
class YourViewModel(private val errorCallback: ErrorCallback) : ViewModel() {
    private val repository = YourRepository(errorCallback)
    fun fetchData() = repository.fetchData()
}

// Activity
class YourActivity : AppCompatActivity(), ErrorCallback {
    private lateinit var viewModel: YourViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 手动传入回调实例到ViewModel
        viewModel = ViewModelProvider(this, object : ViewModelProvider.Factory {
            override fun <T : ViewModel> create(modelClass: Class<T>): T {
                return YourViewModel(this@YourActivity) as T
            }
        })[YourViewModel::class.java]

        viewModel.fetchData()
    }

    override fun onError(errorMsg: String) {
        // 切换回主线程显示Toast
        runOnUiThread {
            Toast.makeText(this, errorMsg, Toast.LENGTH_SHORT).show()
        }
    }

    override fun onDestroy() {
        // 如果有需要,这里可以清理回调防止内存泄漏
        super.onDestroy()
    }
}

最后提醒:

优先选LiveData或Flow,因为它们天生和生命周期绑定,不会出现Activity销毁后还回调的情况,完美符合MVVM的解耦原则。接口回调虽然简单,但需要额外处理内存泄漏,适合小项目或者快速迭代的场景。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 07:44:12