为什么与备份相关的过程可能导致应用程序的 onCreate 未执行?命令 1命令 2命令 3命令 4

2022-09-01 23:42:44

通常有如下类Application

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

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

        me = this;

在正常情况下,'s 将始终在入口点 onCreate 之前被调用。ApplicationonCreateActivity

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());

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

c:\yocto>adb shell bmgr restore com.yocto.wenote
restoreStarting: 1 packages
onUpdate: 0 = com.yocto.wenote
restoreFinished: 0
done

该应用程序将关闭。如果,我点击应用程序图标以再次启动。这就是发生的事情

  1. Application的 未执行!onCreate
  2. Activity的 被执行,并且是onCreateWeNoteApplication.instance()null

我看了一些谷歌的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,...

看来,如果涉及备份相关过程,则不会执行 !ApplicationonCreate

为什么会这样?这种行为是否曾经在某个地方被记录下来?


问题跟踪器

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


错误演示的完整示例

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


答案 1

您可以使用此解决方法绕过您的问题。

这背后的想法是创建自定义来接收事件通知,然后终止进程,因此下次打开应用程序时,系统将创建自定义 Application 类。BackupAgentonRestoreFinished

通常使用自定义强制您实现抽象方法和 ,这些方法和 用于键值备份。幸运的是,如果您在清单中指定,系统将改用基于文件的自动备份,如此所述。BackupAgentonBackuponRestoreandroid: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);
                }
            }
        }
    }
}

然后添加并添加到安卓清单:android:backupAgent="com.yocto.cheok.CustomBackupAgent"android:fullBackupOnly="true"

<?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>

下次您将在恢复后享用该应用程序,您将获得:

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

答案 2

“看来,如果涉及与备份相关的过程,应用程序的 onCreate 将不会执行!”

根据您的陈述,您实际上是对的,其原因清楚地记录在Android文档中

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

两种方式都利用了bmgr工具,基本上自动备份的功能与您所做的相同。

c:\yocto>adb shell bmgr restore com.yocto.wenote

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

文档明确指出

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

即使你的应用使用 bmgr 工具完全还原,它仍然可以处于受限模式(没有可用的自定义应用程序类,而是基类应用程序的实例)。

在此状态下引用自定义应用程序类或从应用中的任何位置引用其中的任何方法肯定会返回 null 引用,因为由于上述语句,它在应用中尚不存在。

您应该通过完全杀死应用程序并再次重新启动应用程序来使应用程序恢复到其默认状态,这是自动备份在幕后所做的最后一件事,您没有通过命令执行该操作。这仅表示在重新启动应用之前未完成命令语句。

--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

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

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

Log.d("MyApplicationLog", "MyApplication --> " + MyApplication.intstance());

在创建上的启动器活动类中添加日志

Log.d("MainActivityLog", "MyApplication --> " +  MyApplication.intstance());

命令 1

--Configure backup transport
c:\me\MyWebApp>adb shell bmgr transport android/com.android.internal.backup.LocalTransport

输出

Selected transport android/com.android.internal.backup.LocalTransport (formerly com.google.android.gms/.backup.BackupTransportService)

命令 2

--Backup app
c:\me\MyWebApp>adb shell bmgr backupnow com.android.webviewapp 

输出

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

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

--Launch app
c:\me\MyWebApp>adb shell monkey -p com.android.webviewapp 1

Logcat 上的输出

enter image description here

命令 3

--Restore app backup
c:\me\MyWebApp>adb shell bmgr restore com.android.webviewapp

输出

restoreStarting: 1 packages
onUpdate: 0 = com.android.webviewapp
restoreFinished: 0
done

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

启动后的输出enter image description here

您可以根据需要多次启动应用程序,自定义应用程序的输出仍为空,直到您运行以下命令

命令 4

--Force close app or kill running process
c:\me\MyWebApp>adb shell am force-stop com.android.webviewapp

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

启动后的输出enter image description here

简单地说:Android操作系统始终假设备份操作仍在进行中,直到应用程序进程重新启动,它不会恢复对应用程序自定义应用程序类的访问。


推荐