Android 开机App更新提示

App更新提示的目的:

由于系统开机的时候会做一些比较耗时的操作, 如果一直显示开机动画, 则会导致用户感觉整个开机流程时间太长, 系统通过显示特定的提示语句来降低用户对开机时间过长的负面体验.

特别耗时的操作:

解压App, 一个App是不能直接加载到内存里运行的, 但是提前对App做处理之后, 可以直接把相关的文件直接加载到内存中.可以降低App首次启动的时间.

代码级分析情景:

1:优化数据存储

一般的复现步骤: 关机时间超过3天, 系统会去做存储空间的优化[由于系统一般在凌晨的时候做这种优化, 理论上开机设置重启不会碰到这个情况]

2:更新了系统, 需要更新

进行了OTA升级, 如果升级到相关的核心组件, 例如 framework.jar, 则会导致大部分的APP都需要优化.

3:偶尔出现

这个是art虚拟机判断某些APP的缓存文件过期, 则会再次进行更新.

4: ART级别的重启类似于V1上重启的原因

原理同V1是一样的, 每次开机, ART虚拟机会记录一次开机次数, 如果连续10次开机失败, 则删除所有的缓存文件, 下次开机会出现转圈的问题.

5: 开机:

测试提到某些机器开机出现转圈, 某些机器开机不出现转圈.

原理:

系统会判断是否是第一次开机, 如果是第一次开机则不转圈, 那么问题就出在是否是第一次开机.
如果 /data/system/packages.xml 或 /data/system/packages_backup.xml 文件存在, 则认为机器不是第一次开机.
这个文件在开机扫描APP的过程中生产, 这个文件生产之后才进行APK的优化.

复现步骤:

机器第一次开机, 开机进行到一半的时候, 这个时候 /data/system/packages.xml 已经生成, 但是APK还没有优化完毕, 这个时候强制关机, 然后再开机, 下次转圈是必现的.

以下是代码分析的关键流程:

1: CustomDialog.show

    PhoneWindowManager.showBootMessage
        WindowManagerService.showBootMessage
                - mAllowBootMessages [mFirstBoot] PackageManagerService - packages.xml and packages-backup.xml [always] [mSystemBooted]
                - mShowingBootMessages
                - mSystemBooted
            ActivityManagerService.showBootMessage(****, [false])
                - PreBootContinuation [正在准备升级***]
                    - Intent.ACTION_PRE_BOOT_COMPLETEDS
                    - [即将完成启动]
                - PackageManagerService.performBootDexOpt() [1: mPackageManagerService.getUsageStatsIfNoPackageUsageInfo();]
                    - isUpgrade (Build.FINGERPRINT) && !isFirstBoot()
                        - [正在优化存储空间] [true]
                        - mPackageDexOptimizer -> sortedPkgs
                        - filterRecentlyUsedApps (sortedPkgs)
                    - PackageManagerService.performBootDexOpt(PackageParser.Package pkg, int curr, int total)
                        - !isFirstBoot()
                        - [*-**][true]
                - ActivityManagerNative.getDefault().showBootMessage[正在启动应用] SystemServer

以下是ART虚拟机的判断APP是否需要更新的逻辑:

// TODO: Verify the dex location is well formed, and throw an IOException if
  // not?

  OatFileAssistant oat_file_assistant(filename, target_instruction_set, false, pkgname);

  // Always treat elements of the bootclasspath as up-to-date.
  if (oat_file_assistant.IsInBootClassPath()) { framework更新
    return OatFileAssistant::kNoDexOptNeeded;
  }

  // TODO: Checking the profile should probably be done in the GetStatus()
  // function. We have it here because GetStatus() should not be copying
  // profile files. But who should be copying profile files?
  if (oat_file_assistant.OdexFileIsOutOfDate()) { 文件缓存超时
    // Needs recompile if profile has changed significantly.
    if (Runtime::Current()->GetProfilerOptions().IsEnabled()) {
      if (oat_file_assistant.IsProfileChangeSignificant()) {
        if (!defer) {
          oat_file_assistant.CopyProfileFile();
        }
        return OatFileAssistant::kDex2OatNeeded;
      } else if (oat_file_assistant.ProfileExists()
          && !oat_file_assistant.OldProfileExists()) { 缓存文件是否存在
        if (!defer) {
          oat_file_assistant.CopyProfileFile();
        }
      }
    }
  }

  return oat_file_assistant.GetDexOptNeeded();

PS: 以上是基于android6.0源码进行分析的.

还有就是 我的flyme 每次更新都会提示更新APP, 理论上 MIUI 和 华为 应该都不会去修改这部分逻辑.

推荐阅读更多精彩内容