Search code examples
androidandroid-6.0-marshmallowbackupmanager

App crashes with ClassCastException on Application class testing M backup


I'm following the guide to specify exclusions from full backups but running into a crash when I try and test it.

$ adb shell bmgr fullbackup <PACKAGE>

Works fine - files are excluded as expected.

I clear data then run:

$ adb shell bmgr restore <PACKAGE>

The restore works fine but then the next time I try and run the app I'm getting a ClassCastException:

Caused by: java.lang.ClassCastException: android.app.Application cannot be cast to com.domain.app.MyCustomApplicationClass

It appears that for some reason there is an instance of my application but it's not an instance of the custom application class as specified in the manifest.

Running the app a second time works fine and I can verify that all data was correctly restored.

I am testing this on a debug build and would like to try and resolve this error before pushing the latest changes to production.


Solution

  • The usual cause of this is inducing a manual restore "the wrong way." This is very poorly documented, I'm afraid, but there are different ways of invoking "bmgr restore", one of which will cause exactly the problem you describe.

    (The problem, specifically, is that full-data backup/restore operations currently require that the app be launched with neither its content providers nor any app-defined Application subclass instantiated; instead, you run with a base-class Application instance. Trying to cast back to your declared subclass throws ClassCastException as you might imagine.)

    In the normal course of things, your app is killed following restore. HOWEVER, if you trigger the restore like this:

    adb shell bmgr restore PACKAGE
    

    this does not happen. That particular invocation syntax runs a "my app wants to restore its data 'live' right now; do not kill me before or after" code path, the one that you get via BackupManager.requestRestore(). In this code path the app is intentionally not killed following restore. It's an artifact of the time when key/value was the only backup/restore paradigm, and in that paradigm there are no such Application subclass issues etc.

    You need to make sure that when you trigger a restore via bmgr, you are using the full syntax:

    adb shell bmgr restore TOKEN PACKAGE
    

    This syntax invokes the complete restore-at-install code path, the one that will tear down your app following the restore specifically to avoid trying subsequent execution with a base-class Application.

    'TOKEN' is the identifier of the dataset containing the data you wish to restore. If you are using the local debugging transport, then TOKEN is always "1". If you are using cloud backup, then it will be the device's own current backup dataset identifier if there is one, or the ancestral dataset if the device has not generated one itself. You can see these in the output of

    adb shell dumpsys backup | egrep 'Current:|Ancestral:'
    

    The dataset's identifying TOKEN is the hex string given there.