为什么备份相关的过程可能导致应用程序的onCreate不执行?

Che*_*eng 20 java android android-workmanager

通常有以下Application课程

public class WeNoteApplication extends MultiDexApplication {
    public static WeNoteApplication instance() {
        return me;
    }

    @Override
    public void onCreate() {
        super.onCreate();

        me = this;
Run Code Online (Sandbox Code Playgroud)

在正常情况下,ApplicationonCreate将永远是入口点之前称为Activity小号的onCreate“。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Normally, it will NOT be null.
        android.util.Log.i("CHEOK", "WeNoteApplication -> " + WeNoteApplication.instance());
Run Code Online (Sandbox Code Playgroud)

但是,如果在启动应用程序时运行以下命令

c:\yocto>adb shell bmgr restore com.yocto.wenote
restoreStarting: 1 packages
onUpdate: 0 = com.yocto.wenote
restoreFinished: 0
done
Run Code Online (Sandbox Code Playgroud)

该应用程序将关闭。如果是,我点击应用程序图标以再次启动。这是怎么回事

  1. ApplicationonCreate未执行!
  2. ActivityonCreate已执行,并且WeNoteApplication.instance()null

我看一些Google的Android源代码(WorkManager例如)

https://github.com/googlecodelabs/android-workmanager/issues/80

他们在评论中指出,

// 1. The app is performing an auto-backup.  Prior to O, JobScheduler could erroneously
//    try to send commands to JobService in this state (b/32180780).  Since neither
//    Application#onCreate nor ContentProviders have run,...
Run Code Online (Sandbox Code Playgroud)

看来,如果备份相关的工艺涉及,ApplicationonCreate将不被执行!

为什么会这样呢?此行为曾经记录在何处吗?


问题追踪器

https://issuetracker.google.com/issues/138423608


错误演示的完整示例

https://github.com/yccheok/AutoBackup-bug

Mat*_*ini 7

您可以使用此替代方法来绕过您的问题。

其背后的想法是创建一个自定义BackupAgent以接收onRestoreFinished事件通知,然后终止您的进程,因此,下次您打开该应用程序时,系统将创建您的自定义Application类。

通常使用自定义BackupAgent强制您实现用于键值备份的抽象方法onBackuponRestore。幸运的是,如果你指定android:fullBackupOnly在清单中,该系统将使用基于文件的自动备份,而不是作为解释在这里

首先,创建自定义BackupAgent

package com.yocto.cheok;

import android.app.ActivityManager;
import android.app.backup.BackupAgent;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import android.os.Process;

import java.util.List;

public class CustomBackupAgent extends BackupAgent {

    private Boolean isRestoreFinished = false;

    @Override
    public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) {
        //NO-OP - abstract method
    }

    @Override
    public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) {
        //NO-OP - abstract method
    }

    @Override
    public void onRestoreFinished() {
        super.onRestoreFinished();

        isRestoreFinished = true;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        if (isRestoreFinished) {
            ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

            if (activityManager != null) {
                final List<ActivityManager.RunningAppProcessInfo> runningServices = activityManager.getRunningAppProcesses();

                if (runningServices != null &&
                        runningServices.size() > 0 &&
                        runningServices.get(0).processName.equals("com.yocto.cheok")
                ) {
                    Process.killProcess(runningServices.get(0).pid);
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后将android:backupAgent="com.yocto.cheok.CustomBackupAgent"和添加android:fullBackupOnly="true"到Android清单中:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.yocto.cheok">

    <application
        android:name="com.yocto.cheok.CheokApplication"
        android:allowBackup="true"
        android:backupAgent="com.yocto.cheok.CustomBackupAgent"
        android:fullBackupContent="@xml/my_backup_rules"
        android:fullBackupOnly="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name="com.yocto.cheok.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>
Run Code Online (Sandbox Code Playgroud)

下次您将在还原后对应用程序进行午餐时,您将获得:

2019-07-28 22:25:33.528 6956-6956/com.yocto.cheok I/CHEOK: CheokApplication onCreate
2019-07-28 22:25:33.642 6956-6956/com.yocto.cheok I/CHEOK: In MainActivity, CheokApplication = com.yocto.cheok.CheokApplication@7b28a29
Run Code Online (Sandbox Code Playgroud)


Gid*_*aya 7

“看来,如果涉及到备份相关过程,则将不会执行Application的onCreate!”

根据您的陈述,您实际上是正确的,其原因已在android docs中明确记录。

Android为应用程序提供了两种备份数据的方式: 自动备份应用程序键/值备份

两种方法都使用bmgr工具,并且基本上自动备份的功能与您所做的相同。

c:\yocto>adb shell bmgr restore com.yocto.wenote
Run Code Online (Sandbox Code Playgroud)

还原后不存在“自定义应用程序”类,为什么会这样?

文件明确指出,

在自动备份和还原操作期间,系统以受限模式启动应用程序,以防止应用程序访问可能引起冲突的文件,并让应用程序在其BackupAgent中执行回调方法 。在此受限模式下,不会自动启动应用程序的主要活动,不初始化其内容提供程序,并且 实例化基类Application而不是在应用程序清单中声明的任何子类

即使使用bmgr工具完全还原了您的应用程序,它仍可以处于受限模式下(没有提供其Custom Application类,但有一个基类Application的实例)。

在此状态下引用您的Custom Application类或从应用程序中任何位置引用其中的任何方法,肯定会返回空引用,因为由于上述原因,您的应用程序中尚不存在空引用。

您应通过完全杀死应用程序并再次重新启动它来使应用程序恢复默认状态,这是自动备份在您未通过命令执行的情况下要做的最后一件事。这只是意味着您的命令语句在重新启动应用程序之前尚未完成。

--Kill app process and restart app

c:\yocto>adb shell am force-stop com.yocto.wenote
c:\yocto>adb shell monkey -p com.yocto.wenote 1
Run Code Online (Sandbox Code Playgroud)

以下是基于您使用Android Studio IDE和带有Android O的设备的代码的测试用例

在自定义应用程序类onCreate中添加日志

Log.d("MyApplicationLog", "MyApplication --> " + MyApplication.intstance());
Run Code Online (Sandbox Code Playgroud)

在启动器活动类onCreate中添加日志

Log.d("MainActivityLog", "MyApplication --> " +  MyApplication.intstance());
Run Code Online (Sandbox Code Playgroud)

命令1

--Configure backup transport
c:\me\MyWebApp>adb shell bmgr transport android/com.android.internal.backup.LocalTransport
Run Code Online (Sandbox Code Playgroud)

输出量

Selected transport android/com.android.internal.backup.LocalTransport (formerly com.google.android.gms/.backup.BackupTransportService)
Run Code Online (Sandbox Code Playgroud)

命令2

--Backup app
c:\me\MyWebApp>adb shell bmgr backupnow com.android.webviewapp 
Run Code Online (Sandbox Code Playgroud)

输出量

Running incremental backup for 1 requested packages.
Package @pm@ with result: Success
Package com.android.webviewapp with progress: 512/1024
Package com.android.webviewapp with progress: 1536/1024
Package com.android.webviewapp with progress: 2048/1024
Package com.android.webviewapp with progress: 2560/1024
Package com.android.webviewapp with result: Success
Backup finished with result: Success
Run Code Online (Sandbox Code Playgroud)

在启动器上手动单击应用程序,或运行与应用程序点击操作同义的Monkey命令

--Launch app
c:\me\MyWebApp>adb shell monkey -p com.android.webviewapp 1
Run Code Online (Sandbox Code Playgroud)

在Logcat上输出

在此处输入图片说明

命令3

--Restore app backup
c:\me\MyWebApp>adb shell bmgr restore com.android.webviewapp
Run Code Online (Sandbox Code Playgroud)

输出量

restoreStarting: 1 packages
onUpdate: 0 = com.android.webviewapp
restoreFinished: 0
done
Run Code Online (Sandbox Code Playgroud)

从启动器中手动单击应用程序,或再次运行上述Monkey命令

启动后输出 在此处输入图片说明

您可以启动应用程序任意次,直到运行以下命令,“自定义应用程序”的输出仍将为null

命令4

--Force close app or kill running process
c:\me\MyWebApp>adb shell am force-stop com.android.webviewapp
Run Code Online (Sandbox Code Playgroud)

从启动器中手动单击应用程序,或再次运行上述Monkey命令

启动后输出 在此处输入图片说明

简而言之:Android OS始终假设备份操作仍在进行中,直到重新启动应用程序进程为止,它不会恢复对应用程序“自定义应用程序”类的访问。