使用 try/catch 来防止应用崩溃

我一直在开发一个Android应用程序,该应用程序经常使用以防止即使在不需要的地方也会崩溃。例如try/catch

中的视图的引用方式如下:xml layoutid = toolbar

// see new example below, this one is just confusing
// it seems like I am asking about empty try/catch
try {
    View view = findViewById(R.id.toolbar);
}
catch(Exception e) {
}

此方法在整个应用中使用。堆栈跟踪未打印,很难找到出错的地方。应用程序突然关闭,而不打印任何堆栈跟踪。

我让我的学长向我解释,他说:

这是为了防止生产中崩溃。

我完全不同意。对我来说,这不是防止应用程序崩溃的方法。这表明开发人员不知道他/她在做什么,并且有疑问。

这是工业中用于防止企业应用程序崩溃的方法吗?

如果真的是我们的需要,那么是否有可能将异常处理程序与UI线程或其他线程附加并捕获所有内容?如果可能的话,这将是一个更好的方法。try/catch

是的,空是不好的,即使我们将堆栈跟踪或日志异常打印到服务器,在所有应用程序中随机包装代码块对我来说也没有意义,例如,当每个函数都包含在.try/catchtry/catchtry/catch

更新

由于这个问题引起了很多关注,有些人误解了这个问题(也许是因为我没有清楚地表达出来),我将重新措辞。

以下是开发人员在这里所做的工作

  • 一个函数是编写和测试的,它可以是一个只是初始化视图的小函数,也可以是一个复杂的函数,在测试之后它被包裹在块周围。即使对于永远不会引发任何异常的函数也是如此。try/catch

  • 这种做法在整个应用程序中使用。有时打印堆栈跟踪,有时只是一些随机错误消息。此错误消息因开发人员而异。debug log

  • 使用此方法,应用不会崩溃,但应用的行为变得不确定。即使有时也很难跟踪出了什么问题。

  • 我一直在问的真正问题是;业界是否遵循防止企业应用程序崩溃的做法?我不是在问空尝试/捕获。是不是像用户喜欢不崩溃的应用程序而不是行为异常的应用程序?因为它实际上归结为要么崩溃它,要么向用户呈现空白屏幕或用户不知道的行为。

  • 我在这里发布一些来自真实代码的片段

      private void makeRequestForForgetPassword() {
        try {
            HashMap<String, Object> params = new HashMap<>();
    
            String email= CurrentUserData.msisdn;
            params.put("email", "blabla");
            params.put("new_password", password);
    
            NetworkProcess networkProcessForgetStep = new NetworkProcess(
                serviceCallListenerForgotPasswordStep, ForgotPassword.this);
            networkProcessForgetStep.serviceProcessing(params, 
                Constants.API_FORGOT_PASSWORD);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
     private void languagePopUpDialog(View view) {
        try {
            PopupWindow popupwindow_obj = popupDisplay();
            popupwindow_obj.showAsDropDown(view, -50, 0);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    void reloadActivity() {
        try {
            onCreateProcess();
        } catch (Exception e) {
        }
    }
    

它不是Android异常处理最佳实践的重复,OP试图为与此问题不同的目的捕获异常。


答案 1

当然,规则总是有例外,但是如果您需要经验法则 - 那么您是对的;空的捕获块是“绝对”的不良做法。

让我们仔细看看,首先从您的具体示例开始:

try {
  View view = findViewById(R.id.toolbar);
}
catch(Exception e) { }

因此,创建了对某些内容的引用;当失败时...没关系;因为该引用一开始就没有被使用!上面的代码绝对是无用的行噪声。或者,编写该代码的人最初是否认为第二个类似的调用会神奇地不再引发异常?!

也许这是为了看起来像这样:

try {
  View view = findViewById(R.id.toolbar);
  ... and now do something with that view variable ...
}
catch(Exception e) { }

但同样,这有什么帮助?!存在异常来分别在代码中传播错误情况。忽略错误很少是一个好主意。实际上,异常可以通过以下方式处理:

  • 您向用户提供反馈;(例如:“您输入的值不是字符串,请重试”);或从事更复杂的错误处理
  • 也许这个问题在某种程度上是预期的,并且可以缓解(例如,当一些“远程搜索”失败时给出“默认”答案)
  • ...

长话短说:你对异常所做的最起码的事情就是记录/跟踪它;这样,当您稍后调试某些问题时,您会明白“好吧,此时发生了异常”。

正如其他人所指出的那样:一般来说,你也避免捕获异常(好吧,取决于层:可能有充分的理由对异常进行一些捕获,甚至在最高级别上捕获某些类型的错误,以确保不会丢失任何内容;曾经)。

最后,让我们引用Ward Cunningham的话

你知道你正在使用干净的代码,当你阅读的每个例程都证明几乎是你所期望的。你可以称它为漂亮的代码,当代码也使它看起来像语言是为问题而制作的。

让它沉浸其中,并冥想它。干净的代码不会让你感到惊讶。您向我们展示的示例让每个观看的人都感到惊讶。

更新,关于OP询问的更新

try {
  do something
}
catch(Exception e) { 
  print stacktrace
}

同样的答案:“到处”这样做也是不好的做法。因为这段代码让读者感到惊讶。

以上:

  • 在某处打印错误信息。根本不能保证这个“某处”类似于一个合理的目的地。恰恰相反。示例:在我正在使用的应用程序内,此类调用将神奇地出现在我们的跟踪缓冲区中。根据上下文,我们的应用程序有时可能会将大量数据泵入这些缓冲区;使这些缓冲区每隔几秒钟修剪一次。因此,“只是打印错误”通常翻译为:“简单地丢失所有此类错误信息”。
  • 然后:你不做尝试/捕捉,因为你可以。你这样做是因为你了解你的代码在做什么;你知道:我最好在这里尝试/捕捉做正确的事情(再次看到我的答案的第一部分)。

因此,使用try/catch作为“模式”,就像您正在展示的那样;就是如所说:还不是一个好主意。是的,它可以防止崩溃;但会导致各种“未定义”的行为。你知道,当你只是抓住一个异常而不是正确地处理它;你打开一罐蠕虫;因为您可能会遇到无数的后续错误,而这些错误后来您不理解。因为您之前使用了“根本原因”事件;在某处打印出来;那个地方现在已经消失了。


答案 2

来自安卓文档

让我们将其授权为 -

不要捕获通用异常

在捕获异常并执行如下操作时,也可能很容易感到懒惰:

try {
    someComplicatedIOFunction();        // may throw IOException
    someComplicatedParsingFunction();   // may throw ParsingException
    someComplicatedSecurityFunction();  // may throw SecurityException
    // phew, made it all the way
} catch (Exception e) {                 // I'll just catch all exceptions
    handleError();                      // with one generic handler!
}

在几乎所有情况下,捕获通用或可抛出(最好不是可抛出,因为它包含错误异常)是不合适的。这是非常危险的,因为这意味着您从未预料到的异常(包括类似)会在应用程序级错误处理中捕获。ExceptionRuntimeExceptionsClassCastException

它掩盖了代码的故障处理属性,这意味着如果有人在调用的代码中添加一种新的 Exception 类型,编译器将无法帮助您意识到需要以不同的方式处理错误

捕获通用异常的替代方法:

  • 在一次尝试后,将每个异常分别作为单独的捕获块捕获。这可能很尴尬,但仍然比捕获所有异常更可取。
    按作者编辑:这个是我的选择。当心在 catch 块中重复太多代码。如果您使用的是 Java 7 或更高版本,请使用多重捕获以避免重复相同的捕获块。
  • 重构代码,使其具有更细粒度的错误处理,并具有多个 try 块。将 IO 与解析分开,在每种情况下分别处理错误。
  • 重新引发异常。很多时候,您不需要在此级别捕获异常,只需让方法引发它即可。

在大多数情况下,您不应该以相同的方式处理不同类型的异常。

格式化/段落从此答案的来源略有修改。

附言:不要害怕例外!!他们是朋友!!!