persistent=true的应用在覆盖安装后出现闪退

安卓 专栏收录该内容
6 篇文章 0 订阅

问题描述

2020-11-20上午发现versionCode=2130923534版本的com.mgtv.ota应用的静默升级存在600多次crash,经过分析,主要来自

(1)java.lang.ClassCastException: com.mgtv.ota.biz.binder.OtaUpdateBinder cannot be cast to com.mgtv.ota.biz.binder.OtaUpdateBinder

(2)Caused by: android.content.res.Resources$NotFoundException: Resource "com.mgtv.ota:bool/abc_action_bar_embed_tabs" (7f030000)  is not a Drawable (color or path): TypedValue{t=0x12/d=0xffffffff a=2 r=0x7f030000}

主要原因来自persistent=true属性的应用进程在第一次被覆盖安装后,其进程pid不会变化,ClassLoader加载资源不符合预期

详见既然:http://project.nunaios.com/browse/IMGO-795

具体现象,如下流程图所示:

 

 

原因

目前针对persistent=true的应用,有如下现象:

(1)Android9.0及以上版本,原生的逻辑是禁止persistent=true的应用调用PMS中的 installPackageLI方法,既不允许该应用被覆盖安装。

 

(2)对于persistent=true的应用,如果其被versioncode较大的应用覆盖安装,则其persistent的属性特征将不存在;

(3)对于persistent=true的应用,其被覆盖安装后,其进程PID不会变化,但是会回调其Application的onCreate方法;

ClassCastException异常

Android类加载器:Android应用程序在运行之前,都需要将类加载到ClassLoader中,具体而言就是ClassLoader将dex文件加载到内存中

 

上图是Android的ClassLoader的关系图,其中

(1)BootClassLoader是ClassLoader内部类,是Android中所有ClassLoader的parent;

(2)BaseDexClassLoader是PathClassLoader、DexClassLoader、InMemoryDexClassLoader的父类,类加载的主要逻辑都是在BaseDexClassLoader完成的;

(3)PathClassLoader只能加载已经安装到Android系统中的apk文件(/data/app目录),是Android默认使用的类加载器;

(4)DexClassLoader可以加载任意目录下的dex/jar/apk/zip文件,比PathClassLoader更灵活,是实现热修复的重点;

对于persistent=true的应用,在其被覆盖安装后,其PathClassLoader会加载/data/app目录下的新的类,也就是说它的类对象是变了的;

如下是在牛奶管家中加了一些测试日志的打印,方便验证这个问题。

底板本_ClassLoader测试.log

覆盖安装_ClassLoader.log

其中日志

(1)“底板本_ClassLoader测试.log”的场景是穹顶未被覆盖的牛奶管家的系统;

(2)“覆盖安装_ClassLoader.log”是在上述场景下启动后,覆盖安装牛奶管家,此时进程牛奶管家的PID没有变化,pm list package -f com.mgtv.surveyor查看的结果是

           package:/data/app/com.mgtv.surveyor-1l2Ds2Xnr75Ab2e4tjIk_A==/base.apk=com.mgtv.surveyor

查看其中的关键日志信息:

(1)“底板本_ClassLoader测试.log”中,在Application的onCreate中增加了ClassLoader的打印信息

20201127_16:45:11   01-01 00:00:05.753   759   759 D penglin : dalvik.system.PathClassLoader[DexPathList[[zip file "/system/app/MgTvSurveyor/MgTvSurveyor.apk"],nativeLibraryDirectories=[/system/app/MgTvSurveyor/lib/arm, /system/fake-libs, /system/app/MgTvSurveyor/MgTvSurveyor.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib, /system/lib, /vendor/lib]]]---232133974
20201127_16:45:11   01-01 00:00:05.753   759   759 E penglin : 当前类对应的ClassLoader:dalvik.system.PathClassLoader[DexPathList[[zip file "/system/app/MgTvSurveyor/MgTvSurveyor.apk"],nativeLibraryDirectories=[/system/app/MgTvSurveyor/lib/arm, /system/fake-libs, /system/app/MgTvSurveyor/MgTvSurveyor.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib, /system/lib, /vendor/lib]]]
20201127_16:45:11   01-01 00:00:05.753   759   759 E penglin : 上个ClassLoader的父亲:java.lang.BootClassLoader@5ec92c4

(2)“覆盖安装_ClassLoader.log”中

11-27 16:51:02.809   759   759 D penglin : dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.mgtv.surveyor-1l2Ds2Xnr75Ab2e4tjIk_A==/base.apk"],nativeLibraryDirectories=[/data/app/com.mgtv.surveyor-1l2Ds2Xnr75Ab2e4tjIk_A==/lib/arm, /system/fake-libs, /data/app/com.mgtv.surveyor-1l2Ds2Xnr75Ab2e4tjIk_A==/base.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib]]]---223452709
11-27 16:51:02.809   759   759 E penglin : 当前类对应的ClassLoader:dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.mgtv.surveyor-1l2Ds2Xnr75Ab2e4tjIk_A==/base.apk"],nativeLibraryDirectories=[/data/app/com.mgtv.surveyor-1l2Ds2Xnr75Ab2e4tjIk_A==/lib/arm, /system/fake-libs, /data/app/com.mgtv.surveyor-1l2Ds2Xnr75Ab2e4tjIk_A==/base.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib]]]
11-27 16:51:02.809   759   759 E penglin : 上个ClassLoader的父亲:java.lang.BootClassLoader@5ec92c4

对比上述两者关键日志信息,发现BootClassLoader是一致的,这也符合预期,因为它是com.android.internal.os.ZygoteInit(zygote进程的ClassLoader)或者com.android.internal.os.RuntimeInit 的ClassLoader(其他进程)

但是其PathClassLoader是不一致的,于是这也就能解释“背景”中的ClassCastException的异常了。

Resources$NotFoundException异常

max_ota_resource3.log

如下是异常信息调用栈

12-02 09:19:49.371  2286  2286 E AndroidRuntime: FATAL EXCEPTION: main
12-02 09:19:49.371  2286  2286 E AndroidRuntime: Process: com.mgtv.ota, PID: 2286
12-02 09:19:49.371  2286  2286 E AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mgtv.ota/com.mgtv.ota.HomeActivity}: android.content.res.Resources$NotFoundException: Drawable com.mgtv.ota:bool/abc_action_bar_embed_tabs with resource ID #0x7f030000
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2778)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.-wrap11(Unknown Source:0)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.os.Handler.dispatchMessage(Handler.java:106)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.os.Looper.loop(Looper.java:164)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.main(ActivityThread.java:6498)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at java.lang.reflect.Method.invoke(Native Method)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)
12-02 09:19:49.371  2286  2286 E AndroidRuntime: Caused by: android.content.res.Resources$NotFoundException: Drawable com.mgtv.ota:bool/abc_action_bar_embed_tabs with resource ID #0x7f030000
12-02 09:19:49.371  2286  2286 E AndroidRuntime: Caused by: android.content.res.Resources$NotFoundException: Resource "com.mgtv.ota:bool/abc_action_bar_embed_tabs" (7f030000) is not a Drawable (color or path): TypedValue{t=0x12/d=0xffffffff a=2 r=0x7f030000}
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.content.res.ResourcesImpl.loadDrawableForCookie(ResourcesImpl.java:773)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.content.res.ResourcesImpl.loadDrawable(ResourcesImpl.java:640)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.content.res.Resources.getDrawableForDensity(Resources.java:877)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.content.res.Resources.getDrawable(Resources.java:819)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.content.Context.getDrawable(Context.java:605)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.android.internal.widget.ToolbarWidgetWrapper.setIcon(ToolbarWidgetWrapper.java:322)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.android.internal.widget.ActionBarOverlayLayout.setIcon(ActionBarOverlayLayout.java:742)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.android.internal.policy.PhoneWindow.setDefaultIcon(PhoneWindow.java:1757)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.Activity.initWindowDecorActionBar(Activity.java:2669)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.Activity.setContentView(Activity.java:2685)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.mgtv.tvos.base.BaseActivity.onCreate(BaseActivity.java:24)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.mgtv.ota.HomeActivity.onCreate(HomeActivity.java:23)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.Activity.performCreate(Activity.java:7037)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.Activity.performCreate(Activity.java:7028)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1214)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2731)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2856)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.-wrap11(Unknown Source:0)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1589)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.os.Handler.dispatchMessage(Handler.java:106)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.os.Looper.loop(Looper.java:164)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at android.app.ActivityThread.main(ActivityThread.java:6498)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at java.lang.reflect.Method.invoke(Native Method)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
12-02 09:19:49.371  2286  2286 E AndroidRuntime:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

在覆盖安装persistent=true的应用后,通过桌面icon打起应用时,icon图标对应的sourceID为十进制2130903040(对应的十六进制为0x7f030000)

(1)第一次通过桌面的ICON拉起OTA应用时,为什么会出现闪退现象?

12-02 09:19:43.437   959   959 E AndroidRuntime: FATAL EXCEPTION: main
12-02 09:19:43.437   959   959 E AndroidRuntime: Process: com.mgtv.ota, PID: 959
12-02 09:19:43.437   959   959 E AndroidRuntime: java.lang.ClassCastException: com.mgtv.ota.biz.binder.OtaUpdateBinder cannot be cast to com.mgtv.ota.biz.binder.OtaUpdateBinder
12-02 09:19:43.437   959   959 E AndroidRuntime:  at com.mgtv.ota.ui.fragment.UpdateBaseFragment$1.onServiceConnected(UpdateBaseFragment.java:46)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:1655)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at android.app.LoadedApk$ServiceDispatcher$RunConnection.run(LoadedApk.java:1684)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at android.os.Handler.handleCallback(Handler.java:790)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at android.os.Handler.dispatchMessage(Handler.java:99)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at android.os.Looper.loop(Looper.java:164)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at android.app.ActivityThread.main(ActivityThread.java:6498)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at java.lang.reflect.Method.invoke(Native Method)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:438)
12-02 09:19:43.437   959   959 E AndroidRuntime:  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:807)

详见上述异常信息的调用链,原因详见上述“ClassCastException异常”;

(2)第二次通过桌面的ICON拉起OTA应用时,为什么会去寻找十进制2130903040的sourceID呢?而不是去寻找system/app/MgTvOta.apk中的icon图标呢?

在OTA应用被覆盖安装的过程中,OTA应用的PathClassLoader更改为了加载/data/app/com.mgtv.ota的路径了,如下日志信息:

12-02 09:19:43.074   959   959 D penglin-ota: dalvik.system.PathClassLoader[DexPathList[[zip file "/data/app/com.mgtv.ota-yuuy6BqchFcVgWxLH6ZABA==/base.apk"],nativeLibraryDirectories=[/data/app/com.mgtv.ota-yuuy6BqchFcVgWxLH6ZABA==/lib/arm64, /system/fake-libs, /data/app/com.mgtv.ota-yuuy6BqchFcVgWxLH6ZABA==/base.apk!/lib/armeabi-v7a, /system/lib, /vendor/lib]]]—153642628

同时,桌面会将OTA应用的对应图标更改为/data/app/com.mgtv.ota的路径的图标,如下日志信息:

12-02 09:19:38.365  1051  1051 E penglin : loadDrawable, resources=android.content.res.Resources@5dc5b6d; value=TypedValue{t=0x3/d=0xf8 "res/mipmap-hdpi-v4/ic_launcher.png" a=2 r=0x7f030000}; id=2130903040; value=TypedValue{t=0x3/d=0xf8 "res/mipmap-hdpi-v4/ic_launcher.png" a=2 r=0x7f030000}

(3)既然桌面会去寻找十进制2130903040的sourceID,为什么不会去/data/app/com.mgtv.ota的路径下寻找呢?

这块应该就是persistent=true属性的原因了,具体源码,我还没找到,以下列出关键证据,如下图可知,

在第二次、第三次和第四次通过桌面ICON图标拉起OTA应用出现闪退时,OTA应用所使用的PathClassLoader依旧使用的为/system/app/MgTvOta/MgTvOta.apk,而该apk下sourceID为2130903040(十六进制对应为0x7f030000)

的内容为一个布尔值。

 

sourceID为2130903040(十六进制对应为0x7f030000)对应的内容图:

 

(4)为什么预置在/system/app/MgTvOta/MgTvOta.apk和覆盖安装的/data/app/com.mgtv.ota应用的ic_launcher.png的sourceid不一致呢?

此处暂没找到确切的证据,但猜测可能是因为OTA应用加入了热修复逻辑所致;

结论

(1)对于persistent=true的应用,在首次被覆盖安装后,其PathClassLoader对象会变化,但是其进程不会重启;

(2)倘若因为异常出现进程重启现象,重启后的进程所使用的PathClassLoader对象指向的依旧是/system/app中的原apk路径;

应对方案

如何解决persistent=true应用的ClassCastException问题?

解决方案:应用自身排查是否存在强制转换类的逻辑,如果有,则使用双目运算符instanceof来做判断,如

if (upgradeService instanceof UpgradeBinder) {
    ((UpgradeBinder) upgradeService).configUpgradeType(UpgradeMyselfService.SILENT_UPGRADE);
    ((UpgradeBinder) upgradeService).setReportListener(new IReportListener() {
......

如下也可以采用instanceof来做处理,以免出现ClassCastException的闪退现象。

如何解决Resources$NotFoundException问题?

解决方案:对于persistent=true的应用,出现这类异常了,解决办法是很难的,可以尝试去更改默认加载的icon图标的sourceid的方案,但是这里面有许多坑,目前无有效解决方案;

但是有如下几点建议:

(1)实际验证,对于MgTvOta应用等,如OTA9版本,其后编译出来的apk,他们的ic_launcher.png的sourceid都是一样的,因此,其被覆盖安装后,

         如果能够修复ClassCastException的问题,则通过ICON拉起OTA应用时,不会出现闪退现象。

(2)对于persistent=true的应用,在后续静默升级时,加强测试把关,在“图标点击拉起应用 + monkey场景 + 功能性场景”角度进行测试。

 

 

 

 

 

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值