Android
AndroidStudio

allowBackup が true のときに Application Class が呼ばれないことがあって困った話

以下のように感じで Application Class のサブクラスを作り、Crashlytics の初期化などの最初の起動時に行いたい処理を記述していたが、何故かこの処理が呼ばれないことがあり調査した。

public class MyApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // ここに起動時に一回だけ行いたい処理(初期化処理など)を置いている
        // ...
    }
}

原因

  • Android 6 以降から Auto Backup というアプリが持つデータを自動的にバックアップする機能がある
  • AndroidManifestからオンオフを設定できるが、何も設定していない場合はオンになる
  • AndroidManifest の設定がオンかつ、Android の設定のバックアップ設定がオンのときに実行される機能
  • これによってバックアップされたデータは、次回インストール時(Google play からインストール時)にデータを自動的に復元する
  • その復元した直後はカスタムで定義した Application Class は呼ばれず、ノーマルな Application Class が呼ばれる(MyApplication Class の処理は呼ばれない)
  • その結果、MyApplication Class 内で Crashlytics の初期化などをしていると、クラッシュしていてもエラーが送られてこないので、クラッシュの存在にすら気が付かない。
  • Google Play Console のエラーログだとアプリとは別の場所から収集されているので確認できる

カスタムで定義した Application Class は呼ばれない?どういうこと?

公式のドキュメントの Auto Backup のドキュメントにの一番下にしれっと書いてある以下の部分が重要。

 In this restricted mode, the app's main activity is not automatically launched, 
its Content Providers are not initialized, 
and the base-class Application is instantiated instead of any subclass declared in the app's manifest.

BackupAgent はどうやら制限モードなる状態で起動されるらしく Acitivty は自動起動されず、Content Provider は生成されない。Android Manifest に記載されたカスタムな Application Class の代わりにノーマルな Application Class が生成されるとのこと。

だいたいの人は BackupAgent なんて実装してないので、そんなの関係ねぇわ! と思うところだが、Auto Backup が自動的にリストアするときに呼ばれるものは FullbackupAgent で、これが BackupAgent を利用している。

Auto Backup のリストアが行われるといったんプロセスが終了されるが、また復活してくる。このプロセスは上記の制限状態とやらで起動している。(そんなバカなと思う方のために再現手順を後述したので、気になる人は試してみよう)

この状態からアプリをタップして起動すると ノーマルな Application Class が読み込まれた状態, Content Provider とかも呼ばれていない状態でアプリが起動するので、それらの状態に依存した処理はおかしな動きをしてしまう。

一部の端末、もしくは一部の Android のバージョンのバグなんじゃないの?

6.0.1, 7.0, 7.1, 8.0, 8.1 の様々な端末で試して再現できたので違う感じがします

対処方法

Auto backup を無効化する

本質的な解決じゃないが・・。手っ取り早く直したいならこれ。以下のような感じで allowBackup=false を追加する。
ライブラリが allowBackup=true に使用してくることがあるので、そのときは tools:replace="allowBackup" も入れないとビルドが通らない。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="net.ore.app">
    ...
    <application
        android:name=".MyApplication"
        android:allowBackup="false" <!-- これを追加した!!!! -->
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:theme="@style/AppTheme"
        tools:replace="allowBackup" <!-- これが必要なケースもある -->
        >
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Auto-backup は有効なままいい感じに対処したい

  • わからん。。
  • Application Class のドキュメント を見ると普通は Application クラスの sub class を作る必要はないと書いてあるので、sub class をそもそも作っていないだけの可能性は十分にある。そうだとすると代わりにどうやればいいんだろうか?

再現手順

Testing Backup and Restoreに記載れているコマンドを使い、バックアップ・復元処理を実行することによって再現できる。

Android の設定でバックアップオプションが有効になっていないのなら有効にする

  • Android の設定を開く
  • 「システム」-> 「バックアップ」と遷移
  • Google Drive へのバックアップのトグルをオンにする
  • バックアップ先のアカウントが選択された状態になっていなければ、それも設定する

コマンドを使って手動でリストアを行う

コマンドを行って復元する過程で様々な理由でちゃんとバックアップ・リストアできないことがある。失敗すると対処方法についても後述したので、なんかうまく行かなかったらそちらを参照してほしい。

  • AndroidManifest.xml の allowbackup が false になっていないことを確認してビルドして端末に入れる
  • 以下のコマンドを実行する必要があるらしいので実行する
    adb shell bmgr backup 「パッケージ名」 && adb shell bmgr run

  • 実際にバックアップを実行する。すると立ち上がっていたプロセスも終了される
    adb shell bmgr fullbackup 「パッケージ名」

  • リストアを行う。
    adb shell bmgr restore 「パッケージ名」

  • リストコマンドを実行した直後、プロセスを一旦終了されるが、何故かまた立ち上がってくる。アプリのタスク一覧には表示されないので Android Device Monitor を利用しないと確認できない。

  • この状態でアプリを起動すると Application Class が無視された状態で起動される

なんか上のコマンドやったのにうまくいかない

コマンド実行後に Logcat を backup とかで grep しつつ観察しよう。一部のトラブルシューティングは公式のドキュメントにも書いてあります。

BackupManagerService: Backup pass but e=false p=true と Logcat に出ているとき

Android の設定でバックアップオプションが有効になっていない。上に手順が書いてあるので有効にしよう

Fullbackup コマンドを実行してもバックアップしてくれない

なにもデータがない状態だとバックアップされません。なので、もちろん restore もできません。Preference 一つでもいいので、データを保存してあげる必要がある

ログが少ない

以下のコマンドを実行しておくと助けになるかもしれない

adb shell setprop log.tag.GmsBackupTransport VERBOSE
adb shell setprop log.tag.BackupXmlParserLogging VERBOSE

他のアプリはどうしている?

AOSP

最後に

  • Auto Backup について調べると、トラブル起きるから allowBackup=false にするとかよく出てくるけど、こんなものが理由だったのか・・。
  • 誰かちゃんと直す方法を教えてくれーー!