はじめに (対象読者・この記事でわかること)
この記事は、Android アプリ開発にチャレンジし始めたばかりの Java 初学者〜中級者を対象にしています。
「Intent を使って次の Activity に遷移したいのに、なぜかアプリが突然落ちる」「Logcat に例外らしきトレースが一切出ず、原因がわからない」という状況で悩んでいる方に最適です。
この記事を読むと、AndroidManifest.xml に遷移先 Activity を「ちゃんと」記載する重要性と、見落としがちな記述ミスを自分で見つけて修正する方法が身につきます。
私も初めてアプリを作ったときにこの現象に 2 時間以上ハマったので、同じ時間を無駄にしたくない方の参考になれば幸いです。
前提知識
- Android Studio でプロジェクトをビルドできる
- Java のクラスやメソッドの基礎知識がある
- Activity 間の遷移に Intent を使うという概念を聞いたことがある
突然のアプリクラッシュ!なのに例外が出ない理由
Android で Intent を使って新しい Activity を起動しようとしたとき、以下のようなコードを書いたとします。
JavaIntent intent = new Intent(MainActivity.this, SubActivity.class); startActivity(intent);
これだけなら教科書通りですが、アプリが即座に強制終了し、Logcat に Exception らしき文字すら表示されない ことがあります。
これは Android ランタイムが「起動しようとした Activity が存在しない」と判断したときの挙動で、ActivityNotFoundException が内部で発生しているにもかかわらず、デフォルトのエラーハンドラでクラッシュダイアログが出ずに落ちる ためです。
原因の 90% 以上は AndroidManifest.xml に遷移先 Activity を宣言し忘れているか、記述が間違っていることです。
原因特定と修正手順(完全保存版)
Step1 まず Logcat のフィルタを「No Filters」にする
Android Studio の Logcat ウィンドウで「Show only selected application」にしていると、クラッシュログが見落とされます。
ツールバーのフィルタを「No Filters」に変更し、ログレベルを「Verbose」にしておきましょう。
このとき、ActivityManager タグに
Unable to find explicit activity class ... have you declared this activity in your AndroidManifest.xml?
という一文が出ていれば、ほぼ原因確定です。
Step2 AndroidManifest.xml をチェックする
app/src/main/AndroidManifest.xml を開き、遷移先の Activity が以下のように記載されているか確認します。
Xml<application ... > <activity android:name=".MainActivity" /> <!-- ここに SubActivity が漏れている! --> </application>
漏れていたら即追加。
Xml<activity android:name=".SubActivity" />
Step3 パッケージ名をフルで書くか、正しい相対パスにする
Kotlin や Jetpack Compose プロジェクトではファイルが移動しやすく、.SubActivity のように相対パスを書いたときに実際のパッケージ構造と異なることがあります。
例えば com.example.myapp.feature.sub.SubActivity なら
Xml<activity android:name="com.example.myapp.feature.sub.SubActivity" />
とフルパスで書くか、sourceSet に合わせてパッケージを移動しましょう。
Step4 プロguard(難読化)を使っている場合は keep 規則を追加
リリースビルドで R8/ProGuard を有効にしていると、Activity クラスが難読化されてクラス名が変わり、ActivityNotFoundException が起きることがあります。
proguard-rules.pro に
-keep public class * extends androidx.appcompat.app.AppCompatActivity
を追記しておきましょう。
Step5 即実行アプリを落ちないようにする裏ワザ
開発中は以下のコードを Application#onCreate に仕込んでおくと、クラッシュダイアログが出ずにデバッグ可能です(あくまで開発用)。
Javaif (BuildConfig.DEBUG) { Thread.setDefaultUncaughtExceptionHandler((t, e) -> { Log.e("DEBUG", "uncaught", e); android.os.Process.killProcess(android.os.Process.myPid()); }); }
まとめ
本記事では、Intent による画面遷移でアプリが強制終了する現象の背景と、例外が見えない理由、そして AndroidManifest.xml への正確な Activity 宣言の重要性を解説しました。
- クラッシュログが出ないときは Logcat フィルタを「No Filters」に
- 遷移先 Activity は必ず AndroidManifest.xml に
<activity>タグで宣言 - パッケージ構造が変わったらフルパスで書くかリファクタ
- ProGuard 利用時は keep 規則を忘れずに
この記事を通して、Intent 周りの「落ちるけどログが出ない」という謎現象を即座に解決できるようになりました。
次回は、Jetpack Navigation Component を使った型安全な遷移方法と、DeepLink 対応のテクニックについて深掘りします。
参考資料
- Android 公式ドキュメント - Activities
- StackOverflow - ActivityNotFoundException but no stacktrace
- Android Studio 公式サイト - Debug with Logcat
