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
相关产品推荐
相关产品推荐

