# 【Android】Hyperion-Crashのようなクラッシュ時に立ち上がるアプリを作ってみた

# 今回やったこと

↓ このようなアプリを作りました。

SuicideReporter

ボタンを押すと意図的にアプリをクラッシュさせ、そのタイミングでForegroundServiceを起動させて、ログをストレージに書き込むといったことをしています。

この記事と似たような内容は GitHubREADME.md にも書いてあります。
そっちを見てもらってもいいかも。

なお、タイトルにもありますが、このアプリは Hyperion-Crash を参考にしています。
マジリスペクト!

アナコンダミミッミ

# アプリクラッシュの検出方法

アプリがクラッシュしたタイミングを知るには独自の Thread.UncaughtExceptionHandler をセットしてやればいいみたいです。
↓ こんな感じのコードを Application#onCreate() で呼んでやっています。

internal object CrashReporter {

    fun setup(context: Context) {
        val newHandler = when (val existingHandler = Thread.getDefaultUncaughtExceptionHandler()) {
            null -> Handler(context)
            else -> Wrapper(existingHandler, Handler(context))
        }

        Thread.setDefaultUncaughtExceptionHandler(newHandler)
    }

    private class Handler(private val context: Context) : Thread.UncaughtExceptionHandler {

        override fun uncaughtException(t: Thread, e: Throwable) {
            CrashReporterService.start(this.context, e)
        }
    }

    private class Wrapper(
        private val existing: Thread.UncaughtExceptionHandler,
        private val handler: Handler
    ) : Thread.UncaughtExceptionHandler {

        override fun uncaughtException(t: Thread, e: Throwable) {
            this.handler.uncaughtException(t, e)
            this.existing.uncaughtException(t, e)
        }
    }
}

Hyperion-Crashでは onApplicationCreated() で同じようなコードを呼んでいるのがわかります。
参考: https://github.com/willowtreeapps/Hyperion-Android/blob/develop/hyperion-crash/src/main/java/com/willowtreeapps/hyperion/crash/CrashPlugin.java

私のSuicideReporterでは CrashReporterService を起動させていますが、Hyperion-Crashでは CrashActivity を起動させています。 ↓

@Override
public void uncaughtException(Thread t, Throwable e) {
    CrashActivity.start(context, reportFactory.createReport(e));
}

# process属性について

ここで起動させるActivity・Serviceなんですが、AndroidManifestに android:process=":crash" が記述されています。
このようなコロン始まりのプロセス名を指定してやった場合、別のプロセスとして立ち上がるみたいです。

試しにこのManifestのServiceの android:process

<service
    android:name=".reporter.CrashReporterService"
    android:process=":crash" />

↓ のように指定せずに実行してみると、

<service android:name=".reporter.CrashReporterService" />

クラッシュさせてもServiceが立ち上がってきませんでした。

# アプリ内で使えるストレージのディレクトリについて

ログの保存場所は ↓ のように取得しています。

File(context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS), fileName)

Context#getExternalFilesDir() で取得できるディレクトリ下に保存する場合はパーミッションがいらないみたいですね。
(知らなかったよ 😿)

外部ストレージにファイルを保存する  |  Android デベロッパー  |  Android Developers

私の環境では ↓ のようなパスが取得できました。

/storage/emulated/0/Android/data/net.aridai.suicidereporter/files/Documents/

このディレクトリの中身をPCにコピーするにはターミナルで以下を実行します。

adb pull /storage/emulated/0/Android/data/net.aridai.suicidereporter/files/Documents/ ~/Documents/作業フォルダ/

すると作業フォルダに中身がコピーされます。
(ただし、これはDebugビルドされているときしかできないかも...?)

眠たいので寝ます。
おやすも。