Android での Unity アプリケーションのクラッシュ処理は、クラッシュハンドラーのチェーンとして機能します。チェーン開始時のクラッシュハンドラーが最初にクラッシュを受信し、クラッシュを処理して、チェーン内の次のクラッシュハンドラーに転送することもできます。チェーン内のクラッシュハンドラーの順序は、それらがインストールされた順序によって定義されます。最初にインストールされたクラッシュハンドラーは、クラッシュを受信する最後のクラッシュハンドラーであり、最後にインストールされたクラッシュハンドラーはクラッシュを受信する最初のクラッシュハンドラーです。デフォルトでは、Unity はチェーンの最初のクラッシュハンドラーとして機能します。クラッシュを処理し、デフォルトでは Android システムクラッシュハンドラーであるチェーン内の次のクラッシュハンドラーに転送します。
クラッシュに対して異なる反応を示すように Unity を設定し、独自のカスタムクラッシュハンドラーをチェーンに追加することもできます。このページでは、Unity がクラッシュの処理に使用するメソッドの指定方法と、カスタムクラッシュハンドラーの作成方法について説明します。
Unity のデフォルトのクラッシュ処理動作を使用しない場合は、-androidChainedSignalHandlerBehavior コマンドライン引数を使用して、Unity のクラッシュに対する反応を変更できます。この引数は、以下のいずれかの値を取ります。
| 動作値 | 説明 |
|---|---|
legacy |
ネイティブのクラッシュが発生すると、Unity はクラッシュをラップして Java 例外としてスローします。Unity は、インストールされているクラッシュハンドラーやデフォルトシステムにクラッシュを転送しません。 |
disabled |
ネイティブのクラッシュが発生すると、Unity はそれを無視して、Android はクラッシュをチェーン内の次のクラッシュハンドラーに直接転送します。これは、カスタムクラッシュハンドラーがインストールされている場合はそのハンドラーであり、インストールされていない場合はデフォルトシステムです。 ノート: この値を使用すると、Unity Cloud Diagnostics などの Unity Services はクラッシュを処理しなくなり、レポートもしません。 |
このコマンドライン引数を Unity に渡す方法については、Unity 起動引数の指定を参照してください。
このセクションには、独自のクラッシュハンドラーを作成して設定する方法の例が記載されています。これが正しく機能するためには、Unity の古い機能のクラッシュ処理動作を使用しないでください。詳細については、Unity がクラッシュを処理する方法を指定を参照してください。
以下のコードサンプルは、カスタムクラッシュハンドラーを示しています。IL2CPP スクリプティングバックエンドを使用する場合は、このサンプル cpp ファイルを Unity プロジェクトに直接配置できます。その後、Unity は libil2cpp.cpp の一部としてそのファイルをコンパイルします。Mono スクリプティングバックエンドを使用する場合は、独自の共有ライブラリをコンパイルおよびリンクする必要があります。詳細については、Android 用ネイティブプラグインの作成を参照してください。
android_crash_handler.cpp
#include <android/log.h>
#include <jni.h>
#include <signal.h>
struct sigaction s_PreviousHandler;
bool s_SignalHandlerInstalled;
static void MyCustomHandler(int sig, siginfo_t* info, void* ucontext)
{
__android_log_print(ANDROID_LOG_VERBOSE, "CustomCrashHandler", "Handling signal %d", sig);
s_PreviousHandler.sa_sigaction(sig, info, ucontext);
}
extern "C" void InstallCustomSignalHandlers()
{
struct sigaction Action = {};
Action.sa_sigaction = MyCustomHandler;
// Note: Register more signals if you want.
Action.sa_flags = SIGSEGV;
sigaction(SIGSEGV, &Action, &s_PreviousHandler);
s_SignalHandlerInstalled = true;
}
extern "C" JNIEXPORT void Java_com_unity3d_player_UnityPlayerActivity_InstallCustomSignalHandlersFromJava()
{
InstallCustomSignalHandlers();
}
extern "C" void UninstallCustomSignalHandlers()
{
if (s_SignalHandlerInstalled)
{
sigaction(SIGSEGV, &s_PreviousHandler, nullptr);
s_SignalHandlerInstalled = false;
}
}
クラッシュハンドラーをインストールし、Unity がそれを使用してクラッシュを処理するように設定するには、上記の cpp ファイルで InstallCustomSignalHandlers メソッドを呼び出します。これは C# または Java コードで行うことができますが、Java からこのメソッドを呼び出すのが効率的です。なぜなら、クラッシュは Unity Player の初期化後、C# コードの実行前に発生する可能性があるためです。
以下のコードサンプルは、Java コードから InstallCustomSignalHandlers メソッドを呼び出す方法を示しています。プロジェクトに追加するには、Java ファイルをプラグインとしてインストールする (Java または Kotlin ソースプラグインの作成を参照) か、エクスポートしたプロジェクトの既存の Java ファイルを変更します。
ノート: このメソッドを呼び出す場所に応じて、クラッシュ処理の動作が変更されます。Unity ランタイム初期化 (mUnityPlayer = new UnityPlayer(this, this); を含むコード行) の前に呼び出した場合は、ネイティブクラッシュ時に Unity のクラッシュハンドラーが最初に実行され、次に信号ハンドラーが実行されます (Unity が信号を転送した場合)。Unity ランタイム初期化後に InstallCustomSignalHandlers を呼び出す場合は、ネイティブクラッシュ時にハンドラーが最初に実行され、信号の転送はユーザーが行う必要があります。
UnityPlayerActivity.java
...
public native void InstallCustomSignalHandlersFromJava();
static
{
System.loadLibrary("il2cpp");
}
// Setup activity layout
@Override protected void onCreate(Bundle savedInstanceState)
{
InstallCustomSignalHandlersFromJava();
...
mUnityPlayer = new UnityPlayer(this, this);
setContentView(mUnityPlayer);
mUnityPlayer.requestFocus();
}
...