原文地址:http://jeroenmols.com/blog/2017/03/08/appcrash/
很多时候,我看到开发者们不遗余力地避免崩溃。但是未处理的异常真的那么不好吗?空值检查就是最终答案了吗?
实际上,有时候你应该希望你的应用程序崩溃。这篇文章会向你解释为什么要这样,也会给你一些实用技巧。
序言
本文我主要关注简单的空值检查,但是这很容易衍生到其他的边界检查。
空指针检查结构
比如说我们有一个简单的应用程序用来显示一个运动员列表:
public void showPlayers(List<Player> soccerPlayers) {
// some awesome code here
}
理想的情况下,它正常运行,但是如果列表为空的时候会发生什么呢?
显然可以添加一个臭名昭著的空值检查:
public void showPlayers(List<Player> soccerPlayers) {
if (soccerPlayers == null) return;
// some awesome code here
}
都解决了!
但是等等,列表内也可能是空的:
public void showPlayers(List<Player> soccerPlayers) {
if (soccerPlayers == null || soccerPlayers.isEmpty()) return;
// some awesome code here
}
那么如果架构中有5层soccerPlayers都是从上层UI中传值过来呢?我们也需要在每一层重复这些检查嘛?
在你知道它之前,你的空值检查会无处不在!
空值检查的问题
显然的,控制检查让你的代码变的混乱。
但是这不是唯一的问题!因此一旦你使用了它们,你就会在各种地方使用它们!
public void showPlayers(List<Player> soccerPlayers) {
if (soccerPlayers == null) return;
if (myRecyclerView == null) return;
if (myRecyclerView.getAdapter() == null) return;
// some awesome code here
}
甚至当你不需要它们的时候,你也会加上它们!
仔细想想…
这里问题的关键是什么?
一个“无辜”的空值检查可能轻易的掩盖了一个更大更根本的问题
实际上在第一个地方soccerPlayers会是空的吗?或者说你的应用程序中低层级的职责是返回一个空的列表?
当soccerPlayers真的是空的时候会发生什么?明确地给用户一个完整的空页面作为返回,不对吗?
实际上后者意味着在生产环境中你的应用程序会在你无法检查它们时“静静地”停止工作!
解决崩溃
如果一个应用程序进入了一个未曾预料的状态,那么它应该崩溃。这里没有通用的解决办法。
方法不应该为各种可能的情况而检查它们的输入。相反地,你应该仔细考虑实际上输入会是什么,并且只用准备这些就可以了。
如果你的应用程序进入了你未曾想到的状态,那么这不应该是你应该尽快了解的吗?
现在来看看我们钟爱的异常!
未处理的异常很棒因为它们:
- 通过应用程序崩溃的方式来马上提醒你
- 突出了问题而不是静静地掩盖
- 留下了问题的痕迹
- 自动的备份到了你的崩溃日志中
很明显这不意味着你的应用程序应该在你的用户那崩溃!我所说的是如果我的应用程序中有一个问题,我更希望得到一个崩溃日志而不是什么都不知道。
崩溃与否,对终端用户来说都是一样的:他们的应用程序有问题。
令人安心的是你不会因频繁的崩溃而打扰用户!在部署生产环境前,你还有几个安全网络:开发者测试,质检部门,beta测试,阶段性部署等等…
实用技巧
为了阐明这种方式并帮你开始实现它:
- 设计你的应用程序时总是要坚决抵制任何不可控的输入:响应服务,界面数据输入,输入意图…
- 在你的应用程序中确保数据在入口处的完整性。这样非法数据只会发生在你的应用程序之外,你不需要去检查它。
- 如果你不确定错误会发生在什么地方,那么假设它没有!如果你是对的,在测试时你会发现它。
- 如果一个方法不能再生产环境中调用,或只能被调用一次,那么抛出一个IllegalStateException
- 在推送所有你的用户之前,完整的测试。比的用户先发现可能的崩溃。
总结
比起害怕崩溃,你应该拥抱它们,因为它们让你更快的找到了你应用程序中的错误。崩溃不仅让错误变的可见,它们也通过堆栈跟踪提供了一个便捷的方式来调试它们。