例外catch漏れ対策(UncaughtExceptionHandler)
全ての例外を自前でcatchしていれば何の問題もないんだけれども、
もしアプリ内で例外catch漏れしてしまった場合、
Dalvik VM まで例外が通知されて
「〜が予期せず停止しました。やり直してください」
のダイアログが出てアプリが強制終了してしまう。
これはとにかくダサい!!
原因が ぬるぽ とかだともう顔まっか!!
できればこんなプログラムミスはこっそり隠蔽しておきたい!!
そんな都合のいい欲求を満たす方法として、
Thread.UncaughtExceptionHandler という仕組みがあるようだ。
(android固有ではなく、Javaでもともとあるみたい)
アプリ内で予期しない例外発生時の
「開発者にバグ報告する/しない」機能は
この仕組みで実装できるみたい。
※こちらの記事を参考にさせていただきました。
androidアプリのバグ報告システムを考える
発生しうる例外をすべて把握/制御できればベストなのだが、
とりあえずこの処理を保険として実装しておくのはアリかも。
と安直に考えて、テストコード書いてみました。
テストコードの流れ
- ボタン押下で UIスレ or 別スレ で例外発生させる
- catch漏れ例外を uncaughtExceptionHander で catch(ややこしい)
- uncaughtExceptionHander で 例外catch したらアプリ終了
package test.uncaughtexception; import java.lang.Thread.UncaughtExceptionHandler; import android.app.Activity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.LinearLayout; public class MainActivity extends Activity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(createLayout()); Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread thread, Throwable t) { Log.e(getClass().getName(), "### uncaughtException ###", t); // Activity終了 finish(); } }); } private LinearLayout createLayout() { Button buttonUI = new Button(this); buttonUI.setText("UIスレで例外発生"); buttonUI.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { occurErrorUIThread(); } }); Button buttonOther = new Button(this); buttonOther.setText("別スレで例外発生"); buttonOther.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { occurErrorOtherThread(); } }); LinearLayout layout = new LinearLayout(this); layout.addView(buttonUI); layout.addView(buttonOther); return layout; } private void occurErrorUIThread() { throw new RuntimeException("UI"); } private void occurErrorOtherThread() { new Thread(new Runnable() { @Override public void run() { throw new RuntimeException("OTHER"); } }, "OTHER").start(); } }
とーこーろーがー!
実装してみたけどもうまく動作しなかった。
動作環境は 2.3.3(API Level10) です。(※1)
ココからは有識者の方に助けを求めますw
まず、例外が発生したスレッド が「UIスレッドかどうか」で
例外発生->catch後のアプリ挙動が異なってきます。
例えばこんなかんじになる。
case1
例外発生(=UIスレッド)
↓
UncaughtExceptionHandler#uncaughtException で例外検知
↓
アプリ終了(Activity#finish)
↓
アプリ再起動すると 起動時にANR 発生
case2 例外発生(≠UIスレッド) ↓ UncaughtExceptionHandler#uncaughtException で例外検知 ↓ アプリ終了(Activity#finish) ↓ アプリ再起動しても問題なし
case1 はなんだかアプリが正常に終了していないように見えますね。。
ちなみに、case1 で Activity#finish を呼ばなかった場合は
アプリが終了せず、これまた ANR発生 してしまいます。
うーん。
本来どうするのが正しいのだろうか。。
実装してる方、教えていただけると助かります。
これならcatchせずにダイアログ出したほうがまだマシ、なのか。
そもそも例外catch漏れすることがダメなので、
「例外はすべてcatchしましょう」という原点に戻ってくる気もするけどもw
※1 動作環境追記(20110629)