結論
Unityで作ったAndroidアプリを実行する時に、Androidの開発者向けオプションの中にある「アクティビティを保持しない」オプションを使うととても危険です。
ダメ。ゼッタイ。
「アクティビティを保持しない」オプションの存在理由
通常Androidはアプリの中にアクティビティ(画面)が複数あり、アクティビティを切り替えることで画面遷移を行います。とこらが最前面以外のアクティビティはメモリ不足などのために、いつでも破棄される可能性があります。また最前面のアクティビティでも、画面回転で再生成されます。そのため開発者はアクティビティがいつでも破棄されて、いつでも生成していいように実装する必要があります。
しかしながら常にメモリ不足の状態にすることはできないため、アクティビティ保持のstrictモードとして「アクティビティを保持しない」オプションがあります。このオプションを有効にすることで、常に最前面以外のアクティビティがすぐに破棄されるようになります。
Unityで「アクティビティを保持しない」を使うとどうなる?
Unityは通常単一アクティビティのため、通常このオプションが設定されていても影響ありません。強いて言えば一時的なバックグラウンドでも容赦なく破棄されるため、再ロードが発生します。
ではどういう時に問題になるかと言えば、アプリ内でUnityの以外のためにアクティビティを用意し、それを実行中の切り替える必要がある場合です。このような場合、「アクティビティを保持しない」を付けたまま他のアクティビティに遷移するとアプリが突然死(再起動)します。
Unityと「アクティビティを保持しない」で突然死する理由
それはUnityのメインアクティビティである、UnityPlayerNativeActivity
の中の処理に原因があります・・・
public class UnityPlayerNativeActivity extends NativeActivity {
protected UnityPlayer mUnityPlayer;
protected void onDestroy() {
this.mUnityPlayer.quit();
super.onDestroy();
}
}
public class UnityPlayer extends FrameLayout implements a.a {
public void quit() {
// 前略
kill();
}
protected void kill() {
Process.killProcess(Process.myPid());
}
}
_人人人人人人_
> 突然の死 <
 ̄Y^Y^Y^Y^Y ̄
はい、アクティビティが破棄されるOnDestroyのタイミングで「自殺」しています。
つまり
- 他のアクティビティに遷移する
-
UnityPlayerNativeActivity
が破棄される -
OnDestory
のなかで自分のプロセスを殺す - 開こうとしていた他のアクティビティもろとも死ぬ
という流れになります。
回避方法
考えうる回避方法としては、
- UnityPlayerNativeActivityを継承して誤魔化す
- Unity以外のアクティビティを別プロセスで動かして巻き添えを回避
- 別のアクティビティを開く前に
Settings.System.getInt(getContentResolver(), Settings.Global.ALWAYS_FINISH_ACTIVITIES, 0)
を見て有効だったら開かない
などを考えました。
1番はAndroidにはSuperNotCalledException
なるものがあるので、super.OnDestroy
を呼ばないことは難しそうなのと、迂回処理を書くのが難しいので却下。
2番はAndroidManifest.xmlに書くだけですのでお手軽な一方、メモリ空間が別になるので今回の用途からは却下。
ということで結局3番のシステム設定を見て危ないオプションが有効だったら別アクティビティを開かずにエラーとするようにしました。
そもそも・・・
今回のオプションは開発者向けであるので、本来サポート対象外です。
しかしながら開発者自身が有効にしたまま忘れてバグとして扱ったり、一般ユーザーでも「メモリ不足を解消する」という紹介を元に有効にしてしまうことが考えられるため、このオプションが付いている可能性は考慮してあげる必要がありそうです。
筆者自身は付けていなかったものの、他人からのバグ指摘に1日ぐらい時間を費やして調査し上記問題を発見したため、同じ問題にハマる人を出さないためにここに備忘録として書きました。