电脑知识|欧美黑人一区二区三区|软件|欧美黑人一级爽快片淫片高清|系统|欧美黑人狂野猛交老妇|数据库|服务器|编程开发|网络运营|知识问答|技术教程文章 - 好吧啦网

您的位置:首頁技術文章
文章詳情頁

深入理解Android熱修復技術原理之資源熱修復技術

瀏覽:4日期:2022-09-17 16:39:42
目錄一、普遍的實現方式二、資源文件的格式三、運行時資源的解析四、另辟蹊徑的資源修復方案4.1、新增的資源及其導致 id 偏移4.2、內容發生改變的資源4.3、刪除了的資源4.4、對于type的影響五、更優雅地替換AssetManager六、本章小結一、普遍的實現方式

目前市面上的很多資源熱修復方案基本上都是參考了 Instant Run的實現。

簡要說來,Instant Run中的資源熱修復分為兩步:

1.構造一個新的 AssetManager,并通過反射調用 addAssetPath,把這個完 整的新資源包加入到AssetManager中。這樣就得到了一個含有所有新資源的 AssetManager。

2.找到所有之前引用到原有 AssetManager的地方,通過反射,把引用處替換 為 AssetManager。

一個 Android 進程只包含一個 ResTable, ResTable 的成員變量 mPackageGroups 就是所有解析過的資源包的集合。任何一個資源包中都含有 resources.arsc,它記錄了所有資源的id分配情況以及資源中的所有字符串。這些信息是以二進制方式存儲的。底層的AssetManager做的事就是解析這個文件,然后把相關信息存儲到 mPackageGroups 里面。

二、資源文件的格式

整個 resources.arse 文件,實際上是由一個個 ResChunk (以下簡稱 chunk) 拼接起來的。從文件頭開始,每個 chunk 的頭部都是一個 ResChunk_header結構,它指示了這個chunk的大小和數據類型。

深入理解Android熱修復技術原理之資源熱修復技術

通過ResChunk_header中的type成員,可以知道這個chunk是什么類型, 從而就可以知道應該如何解析這個chunko

解析完一個 chunk 后,從這個 chunk + size的位置開始,就可以得到下一個 chunk 起始位置,這樣就可以依次讀取完整個文件的數據內容。

一般來說,一個 resources.arsc 里面包含若干個package,不過默認情況下, 由打包工具aapt 打出來的包只有一個 package。這個 package里包含了 app中的 所有資源信息。

資源信息主要是指每個資源的名稱以及它對應的編號。我們知道,Android中的每個資源,都有它唯一的編號。編號是一個 32 位數字,用十六進制來表示就是0xPPTTEEEE。PP 為 package id, TT 為 type id, EEEE 為 entry id。

它們代表什么?在 resources.arse 里是以怎樣的方式記錄的呢?

對于 package id,每個 package 對應的是類型為 RES_TABLE_PACKAG E_ TYPE 的 ResTable_package 結構體,ResTable_package 結構體的 id 成員變量就表示它的 package id。 對于 type id,每個type對應的是類型為 RES_TABLE_TYPE_SPEC_ TYPE 的 ResTable_typeSpec 結構體。它的id成員變量就是type id。但是,該type id 具體對應什么類型,是需要到package chunk 里的 Type String Pool 中去解析得到的。比如 Type String Pool 中依次有 attr、 drawablex mipmap、layout 字符串。就表示 attr 類型的 type id 為 1, drawable 類型的 type id 為 2, mipmap 類型的 type id 為 3, layout 類型的type id 為 4。所以,每個 type id對應了 Type String Pool里的字符順序 所指定的類型。 對于 entry id,每個 entry表示一個資源項,資源項是按照排列的先后順序 自動被標機編號的。也就是說,一個type里按位置出現的第一個資源項,其 entry id 為0x0000,第二個為 0x0001,以此類推。因此我們是無法直接指定entry id的,只能夠根據排布順序決定。資源項之間是緊密排布的,沒有空隙,但是可以指定資源項為ResTable_type::NO_ENTRY來填入一個空資源。

舉個例子,我們隨便找個帶資源的 apk,用 aapt解析一下,看到其中的一行是:

$ aapt d resources app-debug.apk

 ......

 spec resource 0x7f040019 com.taobao.patch.demo:layout/activity_main: flags=0x00000000

 ......

這就表示,activity_main.xml 這個資源的編號是 0x7f040019。它的 package id 是 0x7f,資源類型的id為0x04, Type String Pool里的第四個字符串正是 layout 類型,而 0x04 類型的第 0x0019 個資源項就是 activity_main 這個資源。

三、運行時資源的解析

默認由 Android SDK 編出來的 apk,是由 aapt 具進行打包的,其資源包的 package id 就是 0x7f。

系統的資源包,也就是 framework-res.jar, package id 為 0x01。

在走到 app的第一行代碼之前,系統就已經幫我們構造好一個已經添加了安裝包資源的 AssetManager 了。

深入理解Android熱修復技術原理之資源熱修復技術

因此,這個 AssetManager里就已經包含了系統資源包以及 app的安裝包,就是 package id 為 0x01 的 framework-res.jar 中的資源和 package id 為 0x7f 的 app 安裝包資源。

如果此時直接在原有 AssetManager 上繼續 addAssetPath的完整補丁包的 話,由于補丁包里面的package id 也是 0x7f,就會使得同一個 package id的包被 加載兩次。這會有怎樣的問題呢?

在 Android L 之后,這是沒問題的,他會默默地把后來的包添加到之前的包的同—個 PackageGroup 下面。

而在解析的時候,會與之前的包比較同一個 type id所對應的類型,如果該類型 下的資源項數目和之前添加過的不一致,會打出一條warning log,但是仍舊加入到該類型的TypeList 中。

在獲取某個 Type的資源時,會從前往后遍歷,也就是說先得到原有安裝包里 的資源,除非后面的資源的config比前面的更詳細才會發生覆蓋。而對于同一個 config 而言,補丁中的資源就永遠無法生效了。所以在 Android L以上的版本,在原有AssetManager 上加入補丁包,是沒有任何作用的,補丁中的資源無法生效。

而在 Android 4.4 及以下版本,addAssetPath只是把補丁包的路徑添加到 了 mAssetPath中,而真正解析的資源包的邏輯是在app第一次執行 AssetManager::getResTable 的時候。

而在執行到加載補丁代碼的時候,getResTable已經執行過了無數次了。這是因為就算我們之前沒做過任何資源相關操作,Android framework里的代碼也會多 次調用到那里。所以,以后即使是addAssetPath,也只是添加到了 mAssetPath, 并不會發生解析。所以補丁包里面的資源是完全不生效的!

所以,像 Instant Run 這種方案,一定需要一個全新的 AssetManager時,然后再加入完整的新資源包,替換掉原有的AssetManager。

四、另辟蹊徑的資源修復方案

而一個好的資源熱修復方案是怎樣的呢?

首先,補丁包要足夠小,像直接下發完整的補丁包肯定是不行的,很占用空間。

而像有些方案,是先進行 bsdiff,對資源包做差量,然后下發差量包,在運行時 合成完整包再加載。這樣確實減小了包的體積,但是卻在運行時多了合成的操作,耗費了運行時間和內存。合成后的包也是完整的包,仍舊會占用磁盤空間。

而如果不采用類似 Instant Run 的方案,市面上許多實現,是自己修改aapt, 在打包時將補丁包資源進行重新編號。這樣就會涉及到修改 Android SDK工具包, 即不利于集成也無法很好地對將來的aapt 版本進行升級。

針對以上幾個問題,一個好的資源熱修復方案,既要保證補丁包足夠小,不在 運行時占用很多資源,又要不侵入打包流程。我們提出了一個目前市面上未曾實現 的方案。

簡單來說,我們構造了一個 package id 為 0x66的資源包,這個包里只包含改變了的資源項,然后直接在原有AssetManager 中 addAssetPath 這個包。然后就可以了。真的這么簡單?

沒錯!由于補丁包的 package id 為 0x66,不與目前已經加載的 0x7f沖突,因 此直接加入到已有的AssetManager中就可以直接使用了。補丁包里面的資源,只包含原有包里面沒有而新的包里面有的新增資源,以及原有內容發生了改變的資源。

而資源的改變包含增加、減少’ 修改這三種情況,我們分別是如何處理的呢?

對于新增資源,直接加入補丁包,然后新代碼里直接引用就可以了,沒什么好說的。 對于減少資源,我們只要不使用它就行了,因此不用考慮這種情況,它也不影響補丁包。 對于修改資源,比如替換了一張圖片之類的情況。我們把它視為新增資源, 在打入補丁的時候,代碼在引用處也會做相應修改,也就是直接把原來使用舊資源 id 的地方變為新 id。

用一張圖來說明補丁包的情況,是這樣的:

深入理解Android熱修復技術原理之資源熱修復技術

圖中綠線表示新增資源。紅線表示內容發生修改的資源。黑線表示內容沒有變 化,但是id 發生改變的資源。x 表示刪除了的資源。

4.1、新增的資源及其導致 id 偏移

可以看到,新的資源包與舊資源包相比,新增了 holo_grey 和 dropdn_item2 資源,新增的資源被加入到 patch中。并分配了 0x66 開頭的資源 id。

而新增的兩個資源導致了在它們所屬的 type 中跟在它們之后的資源 id發生了 位移。比如 holojight, id 由 0x7f020002 變為 0x7f020003,而 abc_dialog 由 0x7f030004 變為 0x7f030003。新資源插入的位置是隨機的,這與每次 aapt打包 時解析xml 的順序有關。發生位移的資源不會加入 patch,但是在 patch的代碼中會調整id 的引用處。

比如說在代碼里,我們是這么寫的

imageView.setImageResource(R.drawable.holo_light);

這個 R.drawable.holojight 是一個int 值,它的值是 aapt指定的,對于開發者 透明,即使點進去,也會直接跳到對應res/drawable/holo_light.jpg,無法查看。不過可以用反編譯工具,看到它的真實值是0x7f020002。所以這行代碼其實等價于:

imageView.setImageResource(0x7f020002);

而當打出了一個新包后,對開發者而言,holojight的圖片內容沒變,代碼引用處也沒變。但是新包里面,同樣是這句話,由于新資源的插入導致的id改變,對于 R.drawable.holojight 的引用已經變成了:

imageView.setImageResource(0x7f020003);

但實際上這種情況并不屬于資源改變,更不屬于代碼的改變,所以我們在對比新舊代碼之前,會把新包里面的這行代碼修正回原來的id。

imageView.setImageResource(0x7f020002);

然后再進行后續代碼的對比。這樣后續代碼對比時就不會檢測到發生了改變。

4.2、內容發生改變的資源

而對于內容發生改變的資源(類型為 layout 的 activity_main,這可能是我們修 改了 activity_main.xml 的文件內容。還有類型為 string 的 no,可能是我們修改了這個字符串的值),它們都會被加入到 patch 中,并重新編號為新 id。而相應的代碼,也會發生改變,比如,

setContentView(R.layout.activity_main);

實際上也就是

setContentView(0x7f030000);

在生成對比新舊代碼之前,我們會把新包里面的這行代碼變為

setContentView(0x6 6020000);

這樣,在進行代碼對比時,會使得這行代碼所在函數被檢測到發生了改變。于是相應的代碼修復會在運行時發生,這樣就引用到了正確的新內容資源。

4.3、刪除了的資源

對于刪除的資源,不會影響補丁包。

這很好理解,既然資源被刪除了,就說明新的代碼中也不會用到它,那資源放在那里沒人用,就相當于不存在了。

4.4、對于type的影響

可以看到,由于 type0x01 的所有資源項都沒有變化,所以整個 type0x01資源都沒有加入到patch 中。這也使得后面的 type 的 id 都往前移了一位。因此 Type String Pool 中的字符串也要進行修正,這樣才能使得 0x01 的 type 指向 drawable, 而不是原來的 attr。

所以我們可以看到,所謂簡單,指的是運行時應用patch變的簡單了。

而真正復雜的地方在于構造 patch 。我們需要把新舊兩個資源包解開,分別解析 其中的resources.arsc 文件,對比新舊的不同,并將它們重新打成帶有新 package id 的新資源包。這里補丁包指定的 package id 只要不是 0x7f 和 0x01就行,可以是 任意0x7f 以下的數字,我們默認把它指定為 0x66。

構造這樣的補丁資源包,需要對整個resources.arsc的結構十分了解,要對二 進制形式的一個一個chunk進行解析分類,然后再把補丁信息一個一個重新組裝成 二進制的chunk。這里面很多工作與 aapt做的類似,實際上開發打包工具的時候也是參考了很多aapt和系統加載資源的代碼。

五、更優雅地替換AssetManager

對于 Android L 以后的版本,直接在原有 AssetManager 上應用 patch就行 了。并且由于用的是原來的AssetManager,所以原先大量的反射修改替換操作就 完全不需要了,大大提高了加載補丁的效率。

但之前提到過,在 Android KK 和以下版本,addAssetPath是不會加載資源 的,必須重新構造一個新的AssetManager 并加入 patch,再換掉原來的。那么我們不就又要和Instant Run —樣,做一大堆兼容版本和反射替換的工作了嗎?

對于這種情況,我們也找到了更優雅的方式,不需要再如此地大費周章。

深入理解Android熱修復技術原理之資源熱修復技術

明顯,這個是用來銷毀 AssetManager并釋放資源的函數,我們來看看它具體做了什么吧。

深入理解Android熱修復技術原理之資源熱修復技術

可以看到,首先,它析構了 native 層的 AssetManager,然后把 java層的 AssetManager 對 native 層的 AssetManager 的引用設為空。

深入理解Android熱修復技術原理之資源熱修復技術

native 層的 AssetManager 析構函數會析構它的所有成員,這樣就會釋放之前加載了的資源。

而現在,java 層的 AssetManager 已經成為了空殼。我們就可以調用它的 init 方法,對它重新進行初始化了!

深入理解Android熱修復技術原理之資源熱修復技術

這同樣是個native方法,

深入理解Android熱修復技術原理之資源熱修復技術

這樣,在執行 init 的時候,會在 native層創建一個沒有添加過資源,并且 mResources 沒有初始化的的 AssetManager。然后我們再對它進行 addAssetPath,之后由于 mResource 沒有初始化過,就可以正常走到解析 mResources的邏輯,加載所有此時add進去的資源了 !

由于我們是直接對原有的 AssetManager進行析構和重構,所有原先對 AssetManager 對象的引用是沒有發生改變的,這樣,就不需要像 Instant Run那樣進行繁瑣的修改了。

順帶一提,類似 Instant Run 的完整替換資源的方案,在替換 AssetManager這一步,也可以采用我們這種方式進行替換,省時省力又省心。

六、本章小結

總結一下,相比于目前市面上的資源修復方式,我們提出的資源修復的優勢在于:

不侵入打包,直接對比新舊資源即可產生補丁資源包。(對比修改 aapt方式的 實現) 不必下發完整包,補丁包中只包含有變動的資源。(對比 Instanat Run,Amigo 等方式的實現) 不需要在運行時合成完整包。不占用運行時計算和內存資源。(對比 Tinker的 實現)

唯一有個需要注意的地方就是,因為對新的資源的引用是在新代碼中,所有資源修復是需要代碼修復的支持的。也因此所有資源修復方案必然是附帶代碼修復的。而 之前提到過,本方案在進行代碼修復前,會對資源引用處進行修正。而修正就是需要 找到舊的資源id,換成新的id。查找舊 id 時是直接對 int值進行替換,所以會找到 0x7f ?????? 這樣的需要替換 id。但是,如果有開發者使用到了 0x7f ??????這樣的數字,而它并非資源id,可是卻和需要替換的id數值相同,這就會導致這個數字 被錯誤地替換。

但這種情況是極為罕見的,因為很少會有人用到這樣特殊的數字,并且還需要碰巧這數字和資源id相等才行。即使出現,開發者也可以用拼接的方式繞過這類數字的產生。所以基本可以不用擔心這種情況,只是需要注意它的存在。

以上就是深入理解Android熱修復技術原理之資源熱修復技術的詳細內容,更多關于Android資源熱修復的資料請關注好吧啦網其它相關文章!

標簽: Android
相關文章:
主站蜘蛛池模板: 灌木树苗-绿化苗木-常绿乔木-价格/批发/基地 - 四川成都途美园林 | 防爆电机-高压防爆电机-ybx4电动机厂家-河南省南洋防爆电机有限公司 | 楼承板-钢筋楼承板-闭口楼承板-无锡优贝斯楼承板厂 | T恤衫定做,企业文化衫制作订做,广告T恤POLO衫定制厂家[源头工厂]-【汉诚T恤定制网】 | GEDORE扭力螺丝刀-GORDON防静电刷-CHEMTRONICS吸锡线-上海卓君电子有限公司 | [品牌官网]贵州遵义双宁口腔连锁_贵州遵义牙科医院哪家好_种植牙_牙齿矫正_原华美口腔 | 流水线电子称-钰恒-上下限报警电子秤-上海宿衡实业有限公司 | 铁艺,仿竹,竹节,护栏,围栏,篱笆,栅栏,栏杆,护栏网,网围栏,厂家 - 河北稳重金属丝网制品有限公司 山东太阳能路灯厂家-庭院灯生产厂家-济南晟启灯饰有限公司 | 立式壁挂广告机厂家-红外电容触摸一体机价格-华邦瀛 | 学习虾-免费的学习资料下载平台 雪花制冰机(实验室雪花制冰机)百科 | 低温等离子清洗机(双气路进口)-嘉润万丰| 润滑脂-高温润滑脂-轴承润滑脂-食品级润滑油-索科润滑油脂厂家 | 菏泽商标注册_菏泽版权登记_商标申请代理_菏泽商标注册去哪里 | 粘度计维修,在线粘度计,二手博勒飞粘度计维修|收购-天津市祥睿科技有限公司 | 数控专用机床,专用机床,自动线,组合机床,动力头,自动化加工生产线,江苏海鑫机床有限公司 | 武汉天安盾电子设备有限公司 - 安盾安检,武汉安检门,武汉安检机,武汉金属探测器,武汉测温安检门,武汉X光行李安检机,武汉防爆罐,武汉车底安全检查,武汉液体探测仪,武汉安检防爆设备 | 艺术涂料_进口艺术涂料_艺术涂料加盟_艺术涂料十大品牌 -英国蒙太奇艺术涂料 | 衬四氟_衬氟储罐_四氟储罐-无锡市氟瑞特防腐科技有限公司 | 微型气泵-真空-蠕动-水泵-厂家-深圳市品亚科技有限公司 | 10吨无线拉力计-2吨拉力计价格-上海佳宜电子科技有限公司 | 优宝-汽车润滑脂-轴承润滑脂-高温齿轮润滑油脂厂家 | 大型工业风扇_工业大风扇_大吊扇_厂房车间降温-合昌大风扇 | 窖井盖锯圆机_锯圆机金刚石锯片-无锡茂达金刚石有限公司 | 细砂提取机,隔膜板框泥浆污泥压滤机,螺旋洗砂机设备,轮式洗砂机械,机制砂,圆锥颚式反击式破碎机,振动筛,滚筒筛,喂料机- 上海重睿环保设备有限公司 | 传爱自考网_传爱自学考试网 | 减速机三参数组合探头|TSM803|壁挂式氧化锆分析仪探头-安徽鹏宸电气有限公司 | 石家庄装修设计_室内家装设计_别墅装饰装修公司-石家庄金舍装饰官网 | 小型铜米机-干式铜米机-杂线全自动铜米机-河南鑫世昌机械制造有限公司 | 质构仪_鱼糜弹性仪-上海腾拔仪器科技有限公司 | 污水处理设备,一体化泵站,一体化净水设备-「梦之洁环保设备厂家」 | 隔离变压器-伺服变压器--输入输出电抗器-深圳市德而沃电气有限公司 | 电液推杆生产厂家|电动推杆|液压推杆-扬州唯升机械有限公司 | 隔离变压器-伺服变压器--输入输出电抗器-深圳市德而沃电气有限公司 | 螺纹三通快插接头-弯通快插接头-宁波舜驰气动科技有限公司 | 膏方加工_丸剂贴牌_膏滋代加工_湖北康瑞生物科技有限公司 | 光照全温振荡器(智能型)-恒隆仪器| 一级建造师培训_一建培训机构_中建云筑建造师培训网校 | 北京网站建设首页,做网站选【优站网】,专注北京网站建设,北京网站推广,天津网站建设,天津网站推广,小程序,手机APP的开发。 | 仿古瓦,仿古金属瓦,铝瓦,铜瓦,铝合金瓦-西安东申景观艺术工程有限公司 | 工作心得_读书心得_学习心得_找心得体会范文就上学道文库 | 考试试题_试卷及答案_诗词单词成语 - 优易学 |