web-dev-qa-db-ja.com

アプリのクラッシュを防ぐためのtry / catchの使用

私はAndroidアプリで作業しており、try/catchを頻繁に使用して、必要のない場所でもクラッシュしないようにしています。例えば、

xml layout付きのid = 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) {
}

このアプローチはアプリ全体で使用されます。スタックトレースは出力されず、何が問題なのかを見つけるのは非常に困難です。スタックトレースを印刷せずに、アプリが突然終了します。

私は先輩にそれを私に説明するように頼み、彼は言った、

これは、本番環境でのクラッシュを防ぐためです。

私はまったく同意しません。私にとって、これはアプリのクラッシュを防ぐ方法ではありません。開発者は自分が何をしているかわからないことを知らず、疑わしいことを示しています。

これは、エンタープライズアプリのクラッシュを防ぐために業界で使用されているアプローチですか?

try/catchが本当に、本当に必要な場合、UIスレッドまたは他のスレッドで例外ハンドラーをアタッチし、そこですべてをキャッチすることは可能ですか?可能であれば、より良いアプローチになります。

はい、空のtry/catchは悪いです。スタックトレースまたはログ例外をサーバーに出力しても、コードブロックをtry/catchにランダムにラップすることはすべてのアプリでランダムではありません。すべての関数がtry/catchで囲まれている場合。

更新

この質問は多くの注目を集めており、一部の人々は質問を誤って解釈しているので(おそらく私が明確に言い表していないため)、私はそれを言い換えるつもりです。

ここに開発者がやっていることがあります

  • 関数が記述され、tested、ビューを初期化するだけの小さな関数か、テストがtry/catchブロックにラップされた後の複雑な関数になります。例外を決してスローしない関数であっても。

  • このプラクティスは、アプリケーション全体で使用されます。スタックトレースが出力される場合があり、ランダムエラーメッセージを含むdebug logのみが出力される場合があります。このエラーメッセージは、開発者によって異なります。

  • この方法を使用すると、アプリはクラッシュしませんが、アプリの動作は不確定になります。いつかは、何が悪かったのかを追うのが難しい。

  • 私が尋ねてきた本当の質問は、 エンタープライズアプリケーションのクラッシュを防止するために業界で実践されていることですか?そして、empty try/catch。ユーザーは、予期しない動作をするアプリケーションよりもクラッシュしないアプリケーションを愛していますか?本当にクラッシュするか、ユーザーに空白の画面を表示するか、ユーザーが気付かない動作になってしまうからです。

  • 私は実際のコードからいくつかのスニペットをここに投稿しています

      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) {
        }
    }
    

それはnotの複製です Android例外処理のベストプラクティス 、OPはdifferentこの質問よりも目的。

77
mallaudin

もちろん、ルールには常に例外がありますが、経験則が必要な場合は正しいです。空のcatchブロックは「絶対に」悪い習慣です。

最初に特定の例から始めて、詳しく見てみましょう。

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

したがって、何かへの参照が作成されます。そしてそれが失敗したとき...それは問題ではありません。その参照はそもそも使用されていないからです!上記のコードは絶対に無駄なラインノイズです。または、そのコードを書いた人は、最初に2回目の同様の呼び出しが魔法のように例外をスローしないと最初に仮定しますか?!

たぶん、これは次のように見えることを意図していた:

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

しかし、再び、これは何を助けますか?!コード内のcommunicateエラー状況propagateエラー状況には例外が存在します。エラーを無視することはめったに良い考えではありません。実際、例外は次のような方法で処理できます。

  • ユーザーにフィードバックを提供します。 (たとえば、「入力した値は文字列ではありません。もう一度試してください」);または、より複雑なエラー処理に従事する
  • 問題は何らかの形で予想され、軽減できる場合があります(たとえば、「リモート検索」が失敗したときに「デフォルト」の回答を与えることによって)
  • ...

要するに、例外を使用して行うminimumことは、ログ/トレースすることです。後で何らかの問題をデバッグするときに「OK、この時点で例外が発生した」と理解できるように。

また、他の人が指摘したように、一般的にExceptionのキャッチも避けます(まあ、レイヤーによっては、Exception、および最高レベルのいくつかの種類のエラーでさえ、nothingが失われることを確認する;ever)。

最後に、 quote Ward Cunninghamにしましょう:

あなたが読んだ各ルーチンがあなたが期待したものとほとんど同じであることが判明したとき、あなたはきれいなコードで作業していることを知っています。コードが問題のために言語が作成されたように見える場合にも、美しいコードと呼ぶことができます。

沈み込んで瞑想しましょう。きれいなコードはnot驚きません。あなたが私たちに見せている例は、驚きeverybodyを見ています。

Update、OPが求める更新に関して

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

同じ答え:「あちこちで」ということは、badプラクティスでもあります。このコードはalsoであるため、読者を驚かせます。

上記:

  • エラー情報をどこかに出力します。 まったくないこの「どこか」が合理的に似ていることは保証されていない先。それとは反対に。例:使用しているアプリケーション内で、このような呼び出しはトレースバッファーに魔法のように表示されます。コンテキストに応じて、アプリケーションは大量のデータをこれらのバッファーに送り込む場合があります。それらのバッファーを数秒ごとにプルーニングします。したがって、「単なる印刷エラー」は、「単にそのようなエラー情報をすべて失うだけ」に翻訳されます。
  • 次に、あなたはcanだからtry/catchをしません。あなたのコードが何をしているのか理解しているからです。そして、あなたは知っています:私はここで正しいことをするためにトライ/キャッチを持っている方が良いです(私の答えの最初の部分をもう一度見てください)。

そのため、表示しているようにtry/catchを「パターン」として使用します。言ったとおりです:まだ良い考えではありません。そして、はい、それはクラッシュを防ぎます;しかし、あらゆる種類の「未定義」の動作につながります。あなたは、例外を適切に処理するのではなく、ただキャッチするとき、知っています。ワームの缶を開けます。無数のfollow-onエラーに遭遇する可能性があるからです。以前に「根本原因」イベントを消費したためです。どこかに印刷しました。そのsomewhereはなくなっています。

78
GhostCat

Androidドキュメント から:

資格を与えましょう-

一般的な例外をキャッチしない

また、例外をキャッチするときに怠け者になり、次のようなことをすることもできます。

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!
}

ほとんどすべての場合、汎用のExceptionまたはThrowableをキャッチすることは不適切です(エラー例外が含まれているため、Throwableは望ましくありません)。予期しない例外(RuntimeExceptionsのようなClassCastExceptionを含む)がアプリケーションレベルのエラー処理でキャッチされるため、非常に危険です。

コードの失敗処理プロパティを覆い隠します。つまり、誰かがあなたが呼び出しているコードに新しいタイプのExceptionを追加しても、コンパイラはあなたが必要とすることに気付かないでしょうエラーを異なる方法で処理します

一般的な例外をキャッチする代替案:

  • 1回の試行の後、各例外を個別のcatchブロックとして個別にキャッチします。これは厄介かもしれませんが、すべての例外をキャッチするよりも望ましい方法です。
    著者による編集:これは私の選択です。catchブロックでコードを繰り返しすぎないように注意してください。 Java 7以降を使用している場合は、マルチキャッチを使用して、同じキャッチブロックを繰り返さないようにします。
  • コードをリファクタリングして、複数のtryブロックを使用して、よりきめ細かいエラー処理を行います。解析からIOを分割し、それぞれの場合に個別にエラーを処理します。
  • 例外を再スローします。多くの場合、とにかくこのレベルで例外をキャッチする必要はなく、メソッドにそれをスローさせるだけです。

ほとんどの場合、異なるタイプの例外を同じ方法で処理するべきではありません。

この回答のソースからの書式設定/段落の修正。

追伸例外を恐れないでください!!彼らは友達!!!

15
Paresh P.

他の回答へのコメントとしてこれを掲載しますが、その評判はまだありません。

あなたはそれが悪い習慣であると言っているのは正しいです。実際、あなたが投稿したものは例外に関して異なる種類の悪い習慣を示しています。

  1. エラー処理の欠如
  2. 汎用キャッチ
  3. 意図的な例外なし
  4. ブランケットトライ/キャッチ

私はこの例を通してそれらすべてを説明しようとします。

try {
   User user = loadUserFromWeb();     
   if(user.getCountry().equals("us")) {  
       enableExtraFields();
   }
   fillFields(user);   
} catch (Exception e) { 
}

これは、異なる方法で処理する必要があるいくつかの方法で失敗することがあります。

  1. フィールドは埋められないため、ユーザーには空の画面が表示され、その後...何が表示されますか?なし-エラー処理の欠如。
  2. さまざまなタイプのエラーに違いはありません。インターネットの問題またはサーバー自体の問題(停止、破損した要求、破損した送信など)-一般的なキャッチ。
  3. 現在のシステムがそれを妨害するため、独自の目的で例外を使用することはできません。 -意図的な例外なし
  4. 不要なエラーや予期しないエラー(null.equals(...)など)により、重要なコードが実行されないことがあります。 -ブランケットトライ/キャッチ

ソリューション

(1)まず第一に、黙って失敗することは良いことではありません。障害がある場合、アプリは動作しません。代わりに、問題の解決を試みるか、警告を表示する必要があります。たとえば、「ユーザーデータをロードできませんでした。インターネットに接続していない可能性がありますか?」アプリが本来の動作をしていない場合、それ自体を閉じた場合よりもユーザーにとってイライラします。

(4)ユーザーが不完全な場合、例えば国は不明であり、nullを返します。 equalsメソッドはNullPointerExceptionを作成します。そのNPEが上記のようにスローおよびキャッチされた場合、fillFields(user)メソッドは問題なく実行できますが、呼び出されません。これを防ぐには、nullチェックを含める、実行順序を変更する、またはtry/catchスコープを調整します。 (または、「us」.equals(user.getCountry())のようなコーディングを保存することもできますが、例を提供する必要がありました)。もちろん、他の例外もfillFields()の実行を妨げますが、ユーザーがいない場合は、おそらくそれを実行したくないでしょう。

(1、2、3)Webからロードすると、多くの場合、IOExceptionからHttpMessageNotReadable例外まで、さまざまな例外がスローされます。ユーザーがインターネットに接続していない、バックエンドサーバーに変更があった、またはダウンしている可能性がありますが、catch(Exception)を実行しているのでわかりません-代わりに特定の例外をキャッチする必要があります。このようにいくつかをキャッチすることもできます

try{
   User user = loadUserFromWeb(); //throws NoInternetException, ServerNotAvailableException or returns null if user does not exist
   if(user == null) { 
       throw new UserDoesNotExistException(); //there might be better options to solve this, but it highlights how exceptions can be used.
   }
   fillFields(user);
   if("us".equals(user.getCountry()) {
       enableExtraFields();
   }
} catch(NoInternetException e){
    displayWarning("Your internet conneciton is down :(");
} catch(ServerNotAvailableException e){
    displayWarning("Seems like our server is having trouble, try again later.");
} catch(UserDoesNotExistException e){
    startCreateUserActivity();
}

それで説明できるといいのですが。

少なくとも簡単な修正として、できることは、例外を使用してイベントをバックエンドに送信することです。たとえば、firebaseまたはcrashlyticsを使用します。そうすれば、少なくとも(4)のような問題が原因で、メインアクティビティがユーザーの80%に読み込まれません。

9
Syzygy

これは間違いなく悪いプログラミング手法です。

現在のシナリオから、このようなtrycatchが何百もある場合、アプリケーションをデバッグしないと例外がどこで発生するかさえわかりません。これは、アプリケーションが本番環境にある場合は悪夢です。

ただし、例外がスローされるタイミング(およびその理由)を把握できるように、ロガーを含めることができます。通常のワークフローは変更されません。

...
try {
    View view = findViewById(R.id.toolbar);
}catch(Exception e){
    logger.log(Level.SEVERE, "an exception was thrown", e);
}
...
8
Raman Sahasi

これは悪い習慣です。他の答えはそれを言ったが、私は一歩後退して理解することが重要だと思うだろうwhyそもそも例外がある。

すべての関数には、事後条件 –その関数の実行後にすべてが真でなければならないもののセットがあります。たとえば、ファイルから読み取る関数には、ファイル内のデータがディスクから読み取られて返されるというポスト条件があります。関数が事後条件の1つを満たせなかった場合、例外がスローされます。

関数からの例外を無視する(または単に例外をログに記録することで事実上無視する)ことにより、同意したすべての作業を実際に実行しなくても、その関数で大丈夫だと言っていることになります。これはありそうにないようです。関数が正しく実行されない場合、後続の処理がまったく実行されないという保証はありません。そして、特定の関数が最後まで実行されているかどうかに関係なく、コードの残りが正常に実行される場合、最初にその関数がある理由を疑問に思います。

[空のcatchesで問題ない場合があります。たとえば、ロギングは空のキャッチでラップすることを正当化するものです。ロギングの一部を書き込めなくても、アプリケーションはおそらく正常に動作します。しかし、これらは特別な場合であり、通常のアプリで見つけるために本当に一生懸命働く必要があります。]

重要なのは、実際にアプリを実行し続けるわけではないため、これは悪い習慣であるということです(このスタイルの正当な理由)。技術的にはOSがそれを殺していないのかもしれません。しかし、単に例外を無視しただけでは、アプリがまだ正常に動作しているとは考えられません。そして最悪の場合、実際には害を及ぼす可能性があります(たとえば、ユーザーファイルの破損など)。

7
JoshG79

これは複数の理由で悪いです:

  1. findViewByIdが例外をスローするのは何をしているのですか?キャッチするのではなく、それを修正してください(そして、私はこれを見たことがないので教えてください)。
  2. 特定のタイプの例外をキャッチできる場合は、Exceptionをキャッチしないでください。
  3. 良いアプリはクラッシュしないという信念があります。それは真実ではない。必要な場合、良いアプリがクラッシュします。

アプリが悪い状態になった場合、使用できない状態で追い詰めるよりも、クラッシュする方がはるかに優れています。 NPEが表示された場合、nullチェックに固執して立ち去るべきではありません。より良いアプローチは、何かがnullである理由を見つけてnullになるのを止めるか、(nullが有効で期待される状態になった場合)nullをチェックすることです。ただし、そもそも問題が発生する理由を理解する必要があります。

3
A J

私は過去4〜5年間Androidアプリを開発してきましたが、ビューの初期化にtry catchを使用したことはありません。

ツールバーがこのようになっている場合

Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);

例:-ビュー(フラグメント/ダイアログ/任意のカスタムビュー)からTextViewを取得するには

TextView textview = (TextView) view.findViewById(R.id.viewId);

TextView textview =(TextView)view.findViewById(R.id.viewId);

これの代わりに

View view = findViewById(R.id.toolbar);

ビューオブジェクトは、実際のビュータイプと比較して最小リーチです。

注:-ビューがロードされたためにクラッシュした可能性があります。ただし、try catchを追加するのは悪い習慣です。

2
shijin

前述のように、一般的な例外は、少なくともfew中央の場所(通常はアプリケーションコードではなく、フレームワーク/インフラストラクチャコードにあります)でのみキャッチされるべきではありません。一般的な例外をキャッチしてログに記録する場合は、アプリケーションを後でシャットダウンするか、少なくともユーザーにアプリケーションが不安定な状態にあり、データ破損が発生する可能性があることを通知する必要があります(ユーザーが実行を継続することを選択した場合)。これは、あらゆる種類の例外(名前を付けるためのメモリ不足)をキャッチし、アプリを未定義状態のままにすると発生する可能性があるためです。

私見では、例外を飲み込んでデータの整合性、データ損失のリスクを負うか、アプリをクラッシュさせて、ユーザーに何かがうまくいかなかったことをユーザーに知らせてから再試行するよりも、単にアプリを未定義状態のままにしておくのは悪いことです。これはまた、より良い問題の報告(問題の根本にある)につながります。おそらく、ユーザーが未定義のアプリケーション状態に起因するあらゆる種類のトラブルを報告し始める場合よりも異なる症状は少ないでしょう。

中央の例外処理/ログ/レポートおよび制御されたシャットダウンが行われた後、可能な限り具体的なローカル例外をキャッチするために例外処理の書き直しを開始します。 try {}ブロックをできるだけ短くするようにしてください。

1
DavWEB

もう1つの観点は、日常的にエンタープライズソフトウェアを作成する人として、アプリに回復不可能なエラーがある場合、I wantクラッシュすることです。クラッシュが望ましい。クラッシュすると、ログに記録されます。短時間で数回以上クラッシュする場合、アプリがクラッシュしていることを知らせるメールが届き、アプリと使用するすべてのWebサービスがまだ機能していることを確認できます。

質問:

try{
  someMethod();
}catch(Exception e){}

いい練習?番号!ここにいくつかのポイントがあります:

  1. 最も重要なこと:これは悪い顧客体験です。何か悪いことが起こっていることをどのように知るのですか?私の顧客は私のアプリを使用しようとしていますが、何も機能しません。私のアプリが何をしても、彼らは銀行口座をチェックしたり、請求書を支払ったりすることができません。私のアプリはまったく使い物になりませんが、ちょっと、少なくともクラッシュしませんでした! (私の一部は、この「シニア」開発者がクラッシュ数が少ないとブラウニーポイントを獲得すると考えているため、彼らはシステムを賭けているのです。)

  2. 開発を行っているときに悪いコードを書いているとき、最上層ですべての例外をキャッチして飲み込んでいるだけなら、ロギングはありません。コンソールに何も表示されず、アプリが静かに失敗します。私が言うことができることから、すべてがうまくいくように見えます。だから私はコードをコミットします... DAOオブジェクトは常にnullであり、顧客の支払いは実際にはDBで更新されていませんでした。おっと!しかし、私のアプリはクラッシュしなかったので、それはプラスです。

  3. 悪魔の擁護者を演じて、すべての例外を捕まえて飲み込むことに大丈夫だったとしましょう。 Androidでカスタム例外ハンドラを記述するのは非常に簡単です。本当にhaveですべての例外をキャッチする場合は、コードベース全体にtry/catchをせずに1か所で行うことができます。

私が過去に協力した開発者の一部は、クラッシュが悪いと考えていました。

wantアプリがクラッシュすることを保証する必要があります。いいえ、不安定なアプリは大丈夫ではありませんが、クラッシュとは何か間違ったことをしたため、修正する必要があることを意味します。クラッシュが早くなるほど、それが早く発見され、修正しやすくなります。私が考えることができる他の唯一のオプションは、ユーザーが壊れたセッションを続けることを許可することです。これは、ユーザーベースを怒らせることに相当します。

企業のモバイル開発業界で10年以上働いている男として、私の視点を付け加えさせてください。まず、例外に関するいくつかの一般的なヒント、それらのほとんどは上記の回答に含まれています。

  • 例外は、コード全体を通して定期的にではなく、例外的な、予期しない、または制御できない状況に使用する必要があります。
  • プログラマーは、例外をスローする可能性のあるコードの部分を把握し、それらをキャッチして、残りのコードを可能な限りクリーンにする必要があります。
  • 原則として、例外を黙っておくべきではありません。

現在、自分用にアプリを開発するのではなく、会社や企業向けにアプリを開発する場合、このトピックに関する追加の要件に直面するのが一般的です。

  • 「アプリのクラッシュは会社の悪いイメージを示しているため、受け入れられません」。次に、慎重な開発を実行する必要があります。また、ありそうもない例外をキャッチすることもオプションです。もしそうなら、これは選択的に行われ、合理的な制限内に保たれなければなりません。また、開発はコード行だけではないことに注意してください。たとえば、これらの場合、集中的なテストプロセスが重要です。ただし、企業アプリでの予期しない動作は、クラッシュするよりも悪いです。そのため、アプリで例外をキャッチするたびに、何をすべきか、何を表示し、次にアプリがどのように動作するかを知る必要があります。それを制御できない場合は、アプリをクラッシュさせてください。
  • 「ログとスタックトレースは、機密情報をコンソールにダンプする可能性があります。これは攻撃者に使用される可能性があるため、セキュリティ上の理由から運用環境では使用できません。」この要件は、開発者がサイレントな例外を記述しないという一般的なルールと矛盾するため、そのための方法を見つける必要があります。たとえば、アプリは環境を制御できるため、非実稼働環境ではログとスタックトレースを使用し、実稼働環境ではバグセンス、クラッシュリティクスなどのクラウドベースのツールを使用します。

したがって、簡単な答えは、アプリの品質を改善せずに維持するのは難しく、費用がかかるため、見つけたコードは良い実践例ではないということです。

1
Miguel

はい、try/catchはアプリのクラッシュを防ぐために使用されますが、質問に示されているように、XMLからビューを取得するためにtry/catchは必要ありません。

try/catchは通常、httpリクエストの実行中、StringをURLに解析するとき、URL接続を作成するときなどに使用され、スタックトレースを出力することも確認します。それを印刷しないと、try/catchで囲むのにあまり意味がありません。

本質的にエラーを無視しているため、catch(Exception e){}を使用するのは悪い習慣です。おそらくあなたがしたいことは、次のようなものです:

try {
    //run code that could crash here
} catch (Exception e) {
    System.out.println(e.getMessage());
}
0
ChrisBrownie55

ほぼ同じロジックを使用します。 try-catchを使用して、運用アプリがクラッシュするのを防ぎます。

例外はNEVER無視する必要があります。それは悪いコーディングの習慣です。コードを保守している人は、ログに記録されていない場合に例外を発生させたコードの部分をローカライズするのに非常に苦労します。

Crashlyticsを使用して、例外をログに記録します。コードはクラッシュしません(ただし、一部の機能は中断されます)。ただし、Fabric/Crashlyticsのダッシュボードで例外ログを取得します。これらのログを見て、例外を修正できます。

try {
    codeThatCouldRaiseError();
} catch (Exception e) {
    e.printStackTrace();
    Crashlytics.logException(e);
}
0
K Neeraj Lal

私は他の反応に同意しますが、このモチーフがわずかに許容できる状況に繰り返し遭遇しました。誰かが次のようにクラスのコードを少し書いたとします:

private int foo=0;

    . . .

public int getFoo() throws SomeException { return foo; }

この状況では、 'getFoo()'メソッド失敗しない-返されるプライベートフィールド 'foo'の正当な値が常に存在します。 まだ誰か-おそらく教育的な理由で-おそらく例外をスローする可能性があるとしてこのメ​​ソッドを宣言する必要があることを決定しました。コンテキストでこのメソッドを呼び出そうとした場合。スローされる例外は、基本的にこの構成を使用することを強制されます(その場合でも、少なくとも例外をログに記録する必要があることに同意します)。これをしなければならないときはいつでも、少なくとも 'catch'句の隣に大きなコメント 'THIS CANNOT OCCUR'を常に追加します。

0
PMar