久久综合九色综合97婷婷-美女视频黄频a免费-精品日本一区二区三区在线观看-日韩中文无码有码免费视频-亚洲中文字幕无码专区-扒开双腿疯狂进出爽爽爽动态照片-国产乱理伦片在线观看夜-高清极品美女毛茸茸-欧美寡妇性猛交XXX-国产亚洲精品99在线播放-日韩美女毛片又爽又大毛片,99久久久无码国产精品9,国产成a人片在线观看视频下载,欧美疯狂xxxx吞精视频

有趣生活

當前位置:首頁>職場>深度前端面試題(大廠面試熱點熱修復機制及常見的幾個框架介紹)

深度前端面試題(大廠面試熱點熱修復機制及常見的幾個框架介紹)

發布時間:2024-01-24閱讀(13)

導讀前言現在線上的BUG一直是令很多Android工程師所發愁的問題,可能就是那么幾行代碼,會讓自己所研發的APP損失慘重,所以,熱修復完美的解決了這些問題下面....前言

現在線上的BUG一直是令很多Android工程師所發愁的問題,可能就是那么幾行代碼,會讓自己所研發的APP損失慘重,所以,熱修復完美的解決了這些問題

下面就是我整理總結的一些熱修復知識點和大廠熱修復的一些相關資料

一、什么是熱修復?

熱修復就是一個APP上線發布以后,發現自身存在很多BUG,想要修復這些BUG,但是如果重新推出一個版本、發布、再供用戶下載,那樣所用的時間就太久了,不利用戶體驗,所以熱修復就出來了,他可以在用戶所下載的APP里發布一個插件,他可以在不發布新版本的前提下,修復APP的BUG,這就叫熱修復。

二、熱修復的優勢

深度前端面試題(大廠面試熱點熱修復機制及常見的幾個框架介紹)(1)

三、熱修復機制

dexElements的數組

<pre spellcheck="false" lang="dart" cid="n259" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> /** * List of dex/resource (class path) elements. * Should be called pathElements, but the Facebook app uses reflection * to modify dexElements (http://b/7726934). */ private final Element[] dexElements;</pre>

熱修復就是利用dexElements的順序來做文章,當一個補丁的patch.dex放到了dexElements的第一位,那么當加載一個bug類時,發現在patch.dex中,則直接加載這個類,原來的bug類可能就被覆蓋了

看下PathClassLoader代碼

<pre spellcheck="false" lang="java" cid="n262" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class PathClassLoader extends BaseDexClassLoader { public PathClassLoader(String dexPath, ClassLoader parent) { super(dexPath, null, null, parent); } public PathClassLoader(String dexPath, String libraryPath, ClassLoader parent) { super(dexPath, null, libraryPath, parent); }} </pre>

DexClassLoader代碼

<pre spellcheck="false" lang="dart" cid="n264" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class DexClassLoader extends BaseDexClassLoader { public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) { super(dexPath, new File(optimizedDirectory), libraryPath, parent); }}</pre>

兩個ClassLoader就兩三行代碼,只是調用了父類的構造函數.

<pre spellcheck="false" lang="java" cid="n266" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public class BaseDexClassLoader extends ClassLoader { private final DexPathList pathList; public BaseDexClassLoader(String dexPath, File optimizedDirectory, String libraryPath, ClassLoader parent) { super(parent); this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); } @Override protected Class<?> findClass(String name) throws ClassNotFoundException { List<Throwable> suppressedExceptions = new ArrayList<Throwable>(); Class c = pathList.findClass(name, suppressedExceptions); if (c == null) { ClassNotFoundException cnfe = new ClassNotFoundException("Didnt find class "" name "" on path: " pathList); for (Throwable t : suppressedExceptions) { cnfe.addSuppressed(t); } throw cnfe; } return c; }</pre>

在BaseDexClassLoader 構造函數中創建一個DexPathList類的實例,這個DexPathList的構造函數會創建一個dexElements 數組

<pre spellcheck="false" lang="tsx" cid="n268" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">public DexPathList(ClassLoader definingContext, String dexPath, String libraryPath, File optimizedDirectory) { ... this.definingContext = definingContext; ArrayList<IOException> suppressedExceptions = new ArrayList<IOException>(); //創建一個數組 this.dexElements = makeDexElements(splitDexPath(dexPath), optimizedDirectory, suppressedExceptions); ... }</pre>

然后BaseDexClassLoader 重寫了findClass方法,調用了pathList.findClass,跳到DexPathList類中.

<pre spellcheck="false" lang="dart" cid="n270" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">/* package */final class DexPathList { ... public Class findClass(String name, List<Throwable> suppressed) { //遍歷該數組 for (Element element : dexElements) { //初始化DexFile DexFile dex = element.dexFile; if (dex != null) { //調用DexFile類的loadClassBinaryName方法返回Class實例 Class clazz = dex.loadClassBinaryName(name, definingContext, suppressed); if (clazz != null) { return clazz; } } } return null; } ...} </pre>

會遍歷這個數組,然后初始化DexFile,如果DexFile不為空那么調用DexFile類的loadClassBinaryName方法返回Class實例,歸納上面的話就是:ClassLoader會遍歷這個數組,然后加載這個數組中的dex文件,而ClassLoader在加載到正確的類之后,就不會再去加載有Bug的那個類了,我們把這個正確的類放在Dex文件中,讓這個Dex文件排在dexElements數組前面即可。

四、常見的幾個熱修復框架的對比

熱修復框架的種類繁多,按照公司團隊劃分主要有以下幾種:

類別

成員

阿里系

AndFix、Dexposed、阿里百川、Sophix

騰訊系

微信的Tinker、QQ空間的超級補丁、手機QQ的QFix

知名公司

美團的Robust、餓了么的Amigo

其他

RocooFix、Nuwa、AnoleFix

雖然熱修復框架很多,但熱修復框架的核心技術主要有三類,分別是 代碼修復、資源修復和動態鏈接庫修復,其中每個核心技術又有很多不同的技術方案,每個技術方案又有不同的實現,另外這些熱修復框架仍在不斷的更新迭代中,可見熱修復框架的技術實現是繁多可變的。

部分熱修復框架的對比如下表所示:

特性

AndFix

Tinker/Amigo

QQ空間

Robust/Aceso

即時生效

方法替換

類替換

類結構修改

資源替換

so替換

支持gradle

支持ART

支持Android7.0

我們可以根據上表和具體業務來選擇合適的熱修復框架,當然上表的信息很難做到完全準確,因為部分的熱修復框架還在不斷更新迭代。

五、技術原理及特點5.1 阿里Dexposed -- native

原理:

  • 直接在native層進行方法的結構體信息對換,從而實現完美的方法新舊替換,從而實現熱修復功能
  • 基于開源框架Xposed實現,是一種AOP解決方案
  • 只Hook App本身的進程,不需要Root權限

優點:

  • 即時生效
  • 不需要任何編譯器的插樁或者代碼改寫,對正常運行不引入任何性能開銷。這是AspectJ之類的框架沒法比擬的優勢;
  • 對所改寫方法的性能開銷也極低(微秒級),基本可以忽略不計;
  • 從工程的角度來看,熱補丁僅僅是牛刀小試,它真正的威力在于『線上調試』;
  • 基于Xposed原理實現的AOP不僅可以hook自己的代碼,還可以hook同進程的Android SDK代碼,這也就可以讓我們有能力在App中填上Google自己挖的坑。

缺點:

  • Dalvik上近乎完美,不支持ART(需要另外的實現方式),所以5.0以上不能用了;
  • 最大挑戰在于穩定性與兼容性,而且native異常排查難度更高;
  • 由于無法增加變量與類等限制,無法做到功能發布級別;
5.2 阿里AndFix -- native

原理:

  • 與Dexposed一樣都基于開源框架Xposed實現,是一種AOP解決方案

優點:

  • 即時生效
  • 支持dalvik和art(AndFix supports Android version from 2.3 to 7.0, both ARM and X86 architecture, both Dalvik and ART runtime, both 32bit and 64bit.)
  • 與Dexposed框架相比AndFix框架更加輕便好用,在進行熱修復的過程中更加方便了

缺點:

  • 面臨穩定性與兼容性問題
  • AndFix不支持新增方法,新增類,新增field等
5.3 QQ空間--Dex插樁方案

原理:

  • 原理是Hook了ClassLoader.pathList.dexElements[]。因為ClassLoader的findClass是通過遍歷dexElements[]中的dex來尋找類的。當然為了支持4.x的機型,需要打包的時候進行插樁。
  • 越靠前的Dex優先被系統使用,基于類級別的修復

優點:

  • 不需要考慮對dalvik虛擬機和art虛擬機做適配
  • 代碼是非侵入式的,對apk體積影響不大

缺點:

  • 需要下次啟動才會生效
  • 最大挑戰在于性能,即Dalvik平臺存在插樁導致的性能損耗,Art平臺由于地址偏移問題導致補丁包可能過大的問題
  • 虛擬機在安裝期間為類打上CLASS_ISPREVERIFIED標志是為了提高性能的,我們強制防止類被打上標志是否會影響性能?這里我們會做一下更加詳細的性能測試.但是在大項目中拆分dex的問題已經比較嚴重,很多類都沒有被打上這個標志。
5.4 美團Robust -- Instant Run 熱插拔

原理:

  • Robust插件對每個產品代碼的每個函數都在編譯打包階段自動的插入了一段代碼,插入過程對業務開發是完全透明
  • 編譯打包階段自動為每個class都增加了一個類型為ChangeQuickRedirect的靜態成員,而在每個方法前都插入了使用changeQuickRedirect相關的邏輯,當 changeQuickRedirect不為null時,可能會執行到accessDispatch從而替換掉之前老的邏輯,達到fix的目的。

優點:

  • 幾乎不會影響性能(方法調用,冷啟動)
  • 支持Android2.3-8.x版本
  • 高兼容性(Robust只是在正常的使用DexClassLoader)、高穩定性,修復成功率高達99.9%
  • 補丁實時生效,不需要重新啟動
  • 支持方法級別的修復,包括靜態方法
  • 支持增加方法和類
  • 支持ProGuard的混淆、內聯、優化等操作

缺點:

  • 代碼是侵入式的,會在原有的類中加入相關代碼
  • so和資源的替換暫時不支持
  • 會增大apk的體積,平均一個函數會比原來增加17.47個字節,10萬個函數會增加1.67M。
  • 會增加少量方法數,使用了Robust插件后,原來能被ProGuard內聯的函數不能被內聯了
5.5 微信Tinker

原理:

  • 服務端做dex差量,將差量包下發到客戶端,在ART模式的機型上本地跟原apk中的classes.dex做merge,merge成為一個新的merge.dex后將merge.dex插入pathClassLoader的dexElement,原理類同Q-Zone,為了實現差量包的最小化,Tinker自研了DexDiff/DexMerge算法

Tinker還支持資源和So包的更新,So補丁包使用BsDiff來生成,資源補丁包直接使用文件md5對比來生成,針對資源比較大的(默認大于100KB屬于大文件)會使用BsDiff來對文件生成差量補丁。

優點:

  • 支持動態下發代碼
  • 支持替換So庫以及資源

缺點:

  • 不能即時生效,需要下次啟動

Tinker已知問題:

  • Tinker不支持修改AndroidManifest.xml,Tinker不支持新增四大組件(1.9.0支持新增非export的Activity);
  • 由于Google Play的開發者條款限制,不建議在GP渠道動態更新代碼;
  • 在Android N上,補丁對應用啟動時間有輕微的影響;
  • 不支持部分三星android-21機型,加載補丁時會主動拋出"TinkerRuntimeException:checkDexInstall failed";
  • 對于資源替換,不支持修改remoteView。例如transition動畫,notification icon以及桌面圖標。

Tinker性能痛點:

  • Dex合并內存消耗在vm head上,容易OOM,最后導致合并失敗。
  • 如果本身app占用內存已經比較高,可能容易導致app本系統殺掉。
5.6 阿里Sophix優化Andfix(突破底層結構差異,解決穩定性問題):

Andfix底層ArtMethod結構時采用內部變量一一替換,倒是這個各個廠商是會修改的,所以兼容性不好。

Sophix改變了一下思路,采用整體替換方法結構,忽略底層實現,從而解決兼容穩定性問題。

六、熱修復需要解決的難點

熱修復不同于插件化,不需要考慮各種組件的生命周期,唯一需要考慮的就是如何能將問題的方法/類/資源/so 替換為補丁中的新方法/類/資源/so,其中最重要的是方法和類的替換,所以有不少熱修復框架只做了方法和類的替換,而沒有對資源和 so 進行處理

熱修復框架普遍存在一個問題: 雖然不用安裝新版本的安裝包同樣可以修復bug,但是如果本地下載好的補丁包被刪除了,那么之前bug就會重新!因為熱修復不是合拼生成新的apk,而是 動態加載修復bug的那部分代碼。換句話說修復bug的代碼是存放在補丁包里的,刪除補丁包,修復bug的代碼也就不存在了.之前bug也就重新出來了。

總結

現在的熱修補的技術可以說是百花齊放了,很多大型的公司都有自己完整的熱修復技術框架,但是想要深入了解熱修復,就需要先去了解其中的一些機制,很多機制需要龐大的知識貯備才能進行深入理解,當然Android Framwork的實現細節是非常重要的

熱修復不是簡單的客戶端SDK,它還包含了安全機制和服務端的控制邏輯,整條鏈路也不是短時間可以快速完成的。所以需要我們深入了解才能更好的去理解。

有需要文中完整代碼的同學可以 私信發送 “底層源碼”即可 免費獲取

現在私信還可以獲得 更多《Android 學習筆記+源碼解析+面試視頻》

最后我想說:

對于程序員來說,要學習的知識內容、技術有太多太多,要想不被環境淘汰就只有不斷提升自己,從來都是我們去適應環境,而不是環境來適應我們

技術是無止境的,你需要對自己提交的每一行代碼、使用的每一個工具負責,不斷挖掘其底層原理,才能使自己的技術升華到更高的層面

Android 架構師之路還很漫長,與君共勉

歡迎分享轉載→http://www.avcorse.com/read-233589.html

Copyright ? 2024 有趣生活 All Rights Reserve吉ICP備19000289號-5 TXT地圖HTML地圖XML地圖