Android ROM开发

Android重启后已升级的内置应用被还原

2018-10-19  本文已影响10人  灰灰手记

背景原因

前两天客户报过来一个问题:
机器预置了客户自己开发的 APP,他们在内测的时候发现,APP 在线升级后,只要重启机器,APP 就被还原成一开始预置的版本了……

因没有对我们开放测试渠道,故无法第一时间复现和分析问题;也不清楚客户那边的专业技能水平如何,操作方式是否正确。而我们同 base 的项目从来没有出现过类似问题,我们也没有修改过 APP 升级代码,于是在开始排查代码前,先和客户确认了如下问题:

  1. 在线安装改为本地手动安装是否还有问题?
    原因:APP 升级时采用的是在线静默安装,怀疑可能静默安装有问题。
    反馈:APK 放入机器,手动安装同样有问题。

  2. Settings 里面是否显示为新版本?
    原因:排除 APP 读取版本信息不准确的可能,确认系统真的有处理了升级。
    反馈:重启前,APP 自身、Settings 均显示已升级到新版本;重启后,均显示版本被还原了。

  3. 包名类名是否变化了?
    原因:APP 是 Launcher,而且是默认 Home,若包名类名变化,在重设 Home 前,重启后自动启动原来的 Home ,好像也说得过去。
    反馈:包名类名没有变化。

  4. 开放测试渠道,了解客户APP检测更新,处理升级的流程。
    反馈:客户服务器上有一个路径,APP每次启动都会去看这个路径是否有文件,如果有,就判定为有新版本,然后就下载,静默安装更新。

应用更新规则

做 APP 开发的应该都知道两个 Manifest 属性:versionCodeversionName

早期使用 Eclipse+ADT 开发时,这两个属性是直接定义在 AndroidManifest.xml 中的,而 Manifest 的改动频率也比较高,因此开发人员基本都能注意到。

后面 Google 推了 AS 之后,这两个属性的配置就转到 gradle 脚本了,然后在编译的时候,由脚本自动添加到 AndroidManifest.xml,这时候直接AS上手的可能就容易忽视了,尤其一些没有上架 APP Store 计划的项目。

再回过来说这两个属性,官方API是这么描述的,拿笔记住:

versionCode: Integer, the version code
versionName: String, the version name

versionCode 才是版本号(类是于ID),作为系统判断应用是否能升级的依据,每次版本变化的时候都要随着变化,规则是数字越大版本越新
versionName 只是版本名,是给人看的,方便理解的一串字符串,可以随便定义随便改,怎么写看你自己,就是玩出花来都没人管你。但是,一般都习惯写成 Vx.x.x的形式,有的也在上面加上时间,总之都是为了增加可读性

扯了这么多,反复强调了这么多,相信根据背景原因中的升级策略,也能猜出来是什么问题了。没错,这 APP 前后两版的 versionCode 是一样的,导致重启后,系统在扫描加载应用的时候,直接使用了内置的……

解决方法

对于这个问题,解决方法有两种:一种是修改 APP,按设计标准来;一种是修改 framework,改变APP扫描加载时的判断逻辑。

1、修改 APP

这种方法是标准的处理方式,也是能被普遍接受的方法。方法很简单,每次更新版本时,修改下 versionCode,而不是根据 versionName 判断,更不是像这里一开始说的判断服务器是否有这个文件。

2、修改 framework (代码来源:MTK N0)

此方法不具通用性,仅适用自有 Rom 的情况,其它 Rom 不适用。而且因为修改了被普遍接受的标准的系统逻辑,不排除有引起其它问题的可能。

扫描加载 APP 的代码在 PackageManagerService 中:

\frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java

在这个文件中有这么一个方法:

/**
 *  Scans a package and returns the newly parsed package.
 *  @throws PackageManagerException on a parse error.
 */
private PackageParser.Package scanPackageLI(PackageParser.Package pkg, File scanFile,
        final int policyFlags, int scanFlags, long currentTime, UserHandle user)
        throws PackageManagerException {


在扫描内置应用时,它调用了下面的方法,也就是在这里判断了 versionCode,代码如下:

/**
 *  Scans a package and returns the newly parsed package.
 *  @throws PackageManagerException on a parse error.
 */
private PackageParser.Package scanPackageInternalLI(PackageParser.Package pkg, File scanFile,
        int policyFlags, int scanFlags, long currentTime, UserHandle user)
        throws PackageManagerException {
......

                /** M: [Operator] Allow vendor package downgrade. @{ */
                /// Always install the updated one on data partition
                if (pkg.mVersionCode < ps.versionCode
                       || ((policyFlags & PackageParser.PARSE_IS_OPERATOR) != 0)) {
                /** @} */

......
}

因此要在 framework 层解决这个问题,只需要将上面的 pkg.mVersionCode < ps.versionCode 修改为 pkg.mVersionCode <= ps.versionCode

小结

  1. Android APP升级有标准的,被普遍接受的判断方法,就是以 versionCode 作为判断依据。versionName只是用来增强可读性,让用户能看懂版本变了,不能作为升级判断的依据。

  2. 系统 APP 升级个人这么理解,不一定正确,仅供参考:
    内置的应用在 system 分区,用户安装(升级)的应用在 data 分区。
    当内置应用升级后,system 和 data 各自持有一个版本。
    系统重启,扫描加载APP,以“Always install the updated one on data partition”为原则,判断 system 和 data 两个版本的 versionCode 大小。
    如果 system >= data 则将 system 的拷贝到 data 覆盖掉之前的版本。
    如果 system < data,则继续加载data的版本。

上一篇下一篇

猜你喜欢

热点阅读