热更新总结--资源文件格式

本文章主要根据阿里出的《深入探索Android热修复技术原理》后的个人总结

 

资源打包

 resources.arsc实际上是由一个个的ResChunk拼接起来的,一般来说一个resources.arsc里面包含多个package,但是由AAPT打包只有一个package,这个package包含了APP中的所有资源信息。

每个资源都在APP中都有唯一的编号,编号是一个32位数字,用16进制来表示就是0xPPTTEEEE。PP为 package id,TT是type id,EEEE为entry id。 

 

资源解析

在走到APP第一行代码的时候,系统已经构建好了一个已经加载了安装包中资源的AssetManager了,这个AssetManager里面包含了系统资源包和App的资源包,就是packageid是0x01的framework-res.jar中的资源和package id为0x7f的App安装包资源。

 

此时由于补丁包里面的资源也是0x7f,所以加载两次会产生影响。

 

对于Android L之后,没问题,因为会默默的添加到之前的包的同一个packageGroup下,在解析的时候,会与之前的包进行对比同一个type id所对应的类型,如果类型下的资源项目数和之前添加过的不一致,会打出一条warning log,但是仍旧加入到TypeList中。

 

但是获取资源的时候,由于TypeList中新旧的两个type(drawable layout等)都包含,并且获取entry的时候是从第一个type开始遍历,也就会先获取到原包中的type的entry,代码显示除非补丁包中的资源的config比前面的更详细才会发生覆盖。对于同一个config而言,补丁中的资源是永远无法生效的。所以在Android L以上的版本,在原有AssetManager上加入补丁包,是没有任何作用的,补丁包中的资源无法生效。

 

在Android 4.4版本以下,addAssetPath只是把补丁包的路径添加到了mAssetPath中,而真正解析的资源包逻辑是在App第一次执行AssetManager::getResTable的时候。在执行到加载补丁代码的时候,getResTable已经执行过了无数次,这是因为就算我们之前没做过任何资源相关操作,Android Framework里的代码也会多次调用到这里。所以,以后即使是addAssetPath,也只是添加到了mAssetPath,并不会发生解析。因为补丁包仍旧不会生效。

 

因此像Instant Run这种方案,一定需要一个全新的AssetManager时,再加入完整的新资源包,替换掉原有的AssetManager。

 

Sophix修复方案

由于资源ID前面说个是PP TT EEEE的形式,Type在补丁包中还是在原包中都是相同的,而且也不能修改,所以Sophix的方案是修改pp也就是Package id,修改为0x66,合并资源包,这样资源包就不会冲突,还会将新的资源全部加在进assetmanager中。对于增加,删除和修改的处理:

新增:对于新增资源,直接加入补丁包,然后新代码里直接引用就可以了

减少:我们只要不适用它就行了,因此不会有任何影响。

修改:我们把修改资源视为新增资源,在打入补丁的时候,代码在引用处也会做相应修改,也就是直接把原来的旧ID改成新的ID。

 

相关资源图的替换方案可见书Page136页,很详细。

 

优雅的替换AssetManager

AssetManager的destory方法中有个析构方法,将AssetManager的持有对象先进行析构,然后再执行init方法,会在native层创建一个没有添加过资源,并且mResources没有初始化的AssetManager。然后我们再对它进行addAssetPath,之后由于mResource没有初始化过,就可以正常走到解析mResource的逻辑,加载所有此时加载进去的资源了。

 

由于对原有的AssetManager进行了析构和重构,所以不会对原AssetManager的引用产生影响,这样也就不需要想instant run那样去繁琐的修改了。

 

WebView初始化时对AssetManager的影响

在Android 7.0后,Sophix突然出现了个崩溃。造成的原因是因为WebView的初始化导致0x66下的所有资源都无法被找到。产生问题的原因是,webview首次加载时,由于会调用ResourcesManager.AppendLibAssetForMainPath方法,此时会去找资源是否被加载也就是resourceKey对应的数据是否存在,不存在则去添加资源到AssetManager中。webView添加资源的方式是直接重新建立一个AssetManager然后把resoucesmanager给到AssetManager,这样就会产生问题,原来的加载过补丁包的AssetManager被替换了,导致了错误。

 

解决方案:

1、把补丁包的加载方式也变为WebView这种注入资源的方式?

不可行,因为WebView在createAssetManager时运行时的assets.addAssetPathAsSharedLibrary而不是一直使用addAssetPath方法。由于补丁资源和WebView不一样,不属于lib asset,就无法用这种方式添加资源。

2、是否可以在webview加载完资源之后,再加载一边补丁包资源?

可行,只需要在webview第一次实行初始化后再次添加补丁资源,后面就不会再触发重构逻辑了。但是这个方法需要用户主动去添加,如果这个webview第一次初始化是在sdk中就很难做到。而且开发者并不一定知道时机所在。

3、在加载补丁资源之前先创建一个webview,让它初始化,这样就有了key,就不会在加载资源。

可行,不友好。为用户引入了不一定需要的控件。

4、修改spliteResDirs,将补丁资源反射到LoadApk的mSplitResDirs中

可行,这样不仅开发者没有影响,而且在生成ResourceKey的时候会把相应的资源设置到自己的mSplitResDirs中。

 

具体代码见书157

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值