【发布时间】:2020-07-22 13:12:44
【问题描述】:
目前正在使用 Kotlin 开发 Android 应用程序,我决定学习 MVVM 模式。
我的情况是:
Fragment --> ViewModel --> Controller --> Repository
我的控制器仅用作当前会话的缓存。
目前能够联系我的 API,我想处理可能发生的潜在异常,例如“UnknownHostException”、“SocketTimeoutException”,并为每个异常显示不同的消息。
注意:我使用 retrofit 2.6.0 和 协程 来完成这项工作
向API请求联系的步骤如下:
-
片段订阅 viewModel 变量
-
viewModel 启动它的“fetchCommandList”方法,它启动一个带有 try/catch 的协程并要求控制器返回命令列表。
-
如有必要,控制器会调用存储库。
-
存储库尝试检索 try/catch 块中的数据
这就是我的问题所在。如果您没看错,我的控制器没有 try/catch,但是,如果在存储库中抛出异常,viewModel 会很好地捕获它并更改其 LiveData在片段中显示异常的具体消息。
我不明白异常如何能够“跳过”控制器层并仍然到达 viewModel。
下面是每一层的代码,从视图到模型:
关于错误 LiveData 的片段部分
mviewModel.mErrorStatus.observe(viewLifecycleOwner, Observer {
mbinding.fragmentCommandListRecyclerview.visibility = if (it != null) View.GONE else View.VISIBLE
mbinding.fragmentCommandListErrorTextView.visibility = if (it != null) View.VISIBLE else View.GONE
mbinding.fragmentCommandListErrorTextView.text = it
})
ViewModel 调用控制器
fun fetchCommandList(){
//TODO : contact the controller method tha will firstly check is in list & if not result ask the repository to retrieve it
viewModelScope.launch {
mProgressBar.postValue(true)
try {
mCommandList.postValue(CommandControllerMock.getCommandsList())
mCommandActive.postValue(CommandControllerMock.getCurrentCommand())
}
catch (e: UnknownHostException) {
//this exception occurs when there is no internet connection or host is not available
//so inform user that something went wrong
mErrorStatus.postValue(e.message)
} catch (e: SocketTimeoutException) {
//this exception occurs when time out will happen
//so inform user that something went wrong
mErrorStatus.postValue(e.message)
} catch (e: Exception) {
//this is generic exception handling
//so inform user that something went wrong
mErrorStatus.postValue(e.message)
}
finally {
mProgressBar.postValue(false)
}
}
}
viewModel 调用的控制器 getCommandsList(NO TRY/CATCH)
override suspend fun getCommandsList(): List<Command> {
if(m_commandList.size <= 0){
//Todo: replace by auth.userID
//Todo : before return the result, check if a command is already active to set currentCommand
val returnedList = m_commandRepository.getAllCommandByUser(USER_ID)
returnedList?.let{
m_commandList = it.toMutableList()
}
checkForActiveCommand()
}
return m_commandList
}
调用 API 端点的存储库方法
override suspend fun getAllCommandByUser(idUser: String): List<Command>? {
try {
val response = apiService.getAllCommandByUser(idUser)
if (response.isSuccessful) {
return response.body()
} else {
throw Exception("not 200 code")
}
} catch (e: Exception) {
//this is generic exception handling
//so inform user that something went wrong
throw e
}
}
例如,如果我在手机上禁用了 Internet,由于 UnknownHostException ,我会收到以下消息: “无法解析主机“MY_API_ADRESS”:没有与主机名关联的地址”
如果我在 OkHTTP 中设置了 1 秒的 readTimeout,由于 SocketTimeoutException,我会收到以下消息: “超时”
我只是不明白我忘记在控制器中抛出异常但它仍然被 ViewModel 捕获的部分。
【问题讨论】:
-
在您的存储库中尝试捕获然后在具有
throw e的捕获中具有什么意义? -
我想传播异常,直到 viewModel 处理其中的 UI 显示。存储库只需要将例外提供给较低层
-
那么如果 try catch 捕获异常并抛出相同的东西,它的作用是什么?那么你的 try catch 是多余的
-
我想我不知道异常是如何工作的,对我来说,如果我不在我的存储库中这样做,异常就会丢失。
-
嗯,你可以测试一下。编写一个函数来抛出一个像你这样的 try catch 的异常,然后一个没有,看看它们是不同的还是相同的
标签: android kotlin exception mvvm