多用户情况下,Tinker热修复导致自定义通知栏SystemUI Crash

不积跬步无以至千里

目前我们自己新做的SystemUI的通知栏显示一套框架,对系统的各种问题没有做太多兼容会出现一些问题如下就是发现的一个问题;

一、bug的复现场景:

当切换到分用户时,设置睡眠30分钟(尽量不让手机睡眠),下载QQ浏览器,安装并打开qq浏览器,等待推送通知来,当通知来的时候会显示(安装并打开app时间短的情况),此时很容易就出现直接崩溃到锁屏界面,一直加载锁屏...

二、抓取log:

通知过如下图log

三、log分析

根据log,直观的看见的是SystemUI的通知界面显示加载界面,调用了RemoteView的apply方法,其中apply中调用了inflateView方法,然后在getValue时如下代码抛出异常:

如图代码可以看出其中调用了AssetManager.getResourceValue通过其返回值found,来执行流程,当found返回true时,即找到了资源,否则返回false抛出异常,根据查找文章了解到了其中对资源处理这块,发现调用资源文件时通过AssetManager对资源文件进行处理,但是如果是多线程访问资源的情况,资源会有一个锁,来防止对资源的操作冲突,再者通过整个log分析,发现QQ浏览器刚打开时(低版本,不是最新版本,最新版本不会发生这个问题),发现QQ浏览器的引用了热更新tinker技术,而tinker的热更新技术同样会调用AssetManager来进行文件的操作,占用资源文件,因此当打开QQ浏览器,其实后台正在进行热更新,从而用AssetManager占用了资源文件,当来浏览器来通知的时候,需要调用这个资源文件,但是因为被占用,因此抛出了找不到资源id的问题,从而引发了SystemUI无法引用资源的问题,导致Crash。

四、研究问题结论:

通过上边的分析,其实我们已经知道了问题的所在,是因为占用资源,系统的保护机制加了一个锁,需要等上一个调用者调用完毕,下一个调用者才能调用,从而导致了找不到资源的问题。

五、解决办法:

针对这个问题,我们先解决一下自己的问题,总不能让SystemUI直接崩溃吧。因此我们在RemoteViews.java里添加了如下接口:

为什么要单独添加此接口?

1、咱们这次出问题导致的原因方法是LayoutInflater的inflate方法,因此就是inflateView的这个方法,我们要通过这个方法捕获异常,来间接性发现是否我们需求的资源存在占用的问题,但是为什么要单独拿出来来捕获异常呢?为什么不直接调用apply方法来捕获异常呢?一是我们就直接修改了系统的接口,这样是不友善的;二是我们真正需要的是inflateView这个的捕获异常,但是apply方法里有其他方法,如果你执行这个方法,别的方法同样会调用从而导致逻辑混乱,从而我们拿出这个方法来。

2、上边也提到了,其实添加这个接口的意义就是来判断资源的占用,捕获异常从而进行处理。

接下来我们说一下如何进行我们的防护性处理:

其实咱们的通知有问题,是出在加载数据时导致的问题,但是我们如果想处理的话其实需要在数据源中对有问题的通知数据进行剔除;因此我们在加载数据前,数据源是通过NotificationData获取活跃的通知,这就是数据源,然后通过便利数据然后调用接口查看是否每条数据有无问题。

六、总结:

这样我们就解决了问题了,但是说实在的,其实这是种规避问题的处理方式,如果想真正的决绝问题,应该去找为什么当处于主用户的时候为什么资源占用不会发生问题,从根本上解决问题,才是王道,因为我自身时间和对多用户和资源这块知识的缺乏,未根本性的解决问题,只是从单方面解决了SystemUI遇到这种问题不会崩溃的防护性方式。

七、额外的:

其实系统自带的通知栏是不会出问题的,因为系统的通知栏刷新数据的方法是采用的reapply方法,reapply方法并没有调用inflateView方法,从而不会导致问题。而我们自己的刷新是一直采用apply方法的,这一块需要以后改进。

猜你喜欢

转载自blog.csdn.net/WDYShowTime/article/details/80310474