做程序開發(fā),基礎(chǔ)很重要。同樣是擰螺絲人家擰出來的可以經(jīng)久不壞,你擰出來的遇到點(diǎn)風(fēng)浪就開始顫抖,可見基本功的重要性。再復(fù)雜的技術(shù),也是由一個(gè)一個(gè)簡(jiǎn)單的邏輯構(gòu)成。先了解核心基礎(chǔ),才能更好理解前沿高新技術(shù)。
網(wǎng)站設(shè)計(jì)制作過程拒絕使用模板建站;使用PHP+MYSQL原生開發(fā)可交付網(wǎng)站源代碼;符合網(wǎng)站優(yōu)化排名的后臺(tái)管理系統(tǒng);網(wǎng)站建設(shè)、成都網(wǎng)站設(shè)計(jì)收費(fèi)合理;免費(fèi)進(jìn)行網(wǎng)站備案等企業(yè)網(wǎng)站建設(shè)一條龍服務(wù).我們是一家持續(xù)穩(wěn)定運(yùn)營(yíng)了10年的創(chuàng)新互聯(lián)建站網(wǎng)站建設(shè)公司。
- 先看效果{github Demo地址}:(https://github.com/18598925736/HotUpdateDemo)
- Demo使用方法
- Demo源碼概覽
- 熱修復(fù)核心技術(shù)
- 基礎(chǔ)知識(shí)預(yù)備
- hook思路
- TIPS
熱更新技術(shù),不是新話題。目前最熱門的熱更新由兩種,一種是騰訊tinker為代表的 需重啟app的熱更新,一種是美團(tuán)app為代表的instant Run,無需重啟app. 今天先探究 前者的核心原理。
先看效果[github Demo地址] :(https://github.com/18598925736/HotUpdateDemo)
假如說這是我們的app界面,這個(gè)界面有個(gè)bug,我們直接用一個(gè) TextView
來表示
然而,我們的開發(fā)人員發(fā)現(xiàn)了這個(gè)bug,但是產(chǎn)品已經(jīng)上線。這時(shí)候,由于引起bug的 代碼,只有一行,
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceStata) {
super.onCreate(savedINstanceState);
srtContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.tv);
Bug bug = new Bug():
String s = bug.getstr():
textView.setText(s):
}
}
這個(gè)時(shí)候,機(jī)智的程序員用最快的方式修復(fù)了這個(gè)bug,也只是改了一行代碼:
那么,產(chǎn)品已經(jīng)在線上,怎么辦?我們通過后臺(tái),向app推送了一個(gè) fix.dex
文件, 等這個(gè)文件下載完成,app提示用戶,發(fā)現(xiàn)新的更新,需要重啟app. 待用戶重啟,代碼修復(fù) 即會(huì)生效。無需發(fā)布新版本!
Demo使用方法
下載Demo代碼之后,會(huì)在assets下看到一個(gè)fix.dex
文件
按照正常的邏輯,我們做bug修復(fù)一定是把fix.dex
放到服務(wù)器上, app去服務(wù)器下載它,然后存放在app私有目錄,重啟app之后,fix.dex
生效, 當(dāng)加載到這個(gè)類的時(shí)候,就會(huì)去讀fix.dex
中當(dāng)時(shí)打包的已修復(fù)bug的類. 但是,我這里為了演示方便,直接放在assets,然后使用 項(xiàng)目中的 AssetsFileUtil
類 用io流將它讀寫到 app私有目錄下.
演示方法:
起作用的是誰?就是這個(gè)fix.dex
文件.
如上圖所示: 核心類其實(shí)就只有一個(gè): ClassLoaderHookHelper
,它 就是 讓 fix.dex
這個(gè)補(bǔ)丁發(fā)揮作用的 " 幕后大佬". 這個(gè)核心類:有3個(gè)方法,分別是在不同的系統(tǒng)版本上,來對(duì)源碼程序邏輯進(jìn)行 hook,提高h(yuǎn)ook的兼容性.
下面是完整 ClassLoaderHookHelper
代碼 以及 使用它的 MyApp
完整代碼 :
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Array;
import java.lang.reflect.Field
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ClassLoaderHookHelper {
//23和19的差別,就是 makeXXXElements 方法名和參數(shù)要求不同
//后者是 makeDexElements(ArrayList<File> files, File optimizedDirectory,ArrayList<IOException> suppressedExceptions)
//前者是 makePathElements(List<File> files, File optimizedDirectory,List<IOException> suppressedExceptions)
public static void hookV23(ClassLoader classLoader,File outDexFilePath,File optimizedDirectory)throws IllegalAccessException, InvocationTargetException {
Field pathList =ReflectionUtil.getField(classLoader,"pathList");//1、獲DexPathList pathList 屬性
object dexpathListobj =pathList.get(classLoader);//2、獲DexPathList pathList對(duì)象
Field dexElementsField =ReflectionUtil.getField(dexPathListObj, "dexElements");//3、獲得DexPathList的dexElements屬性
Object[] oldElements =(Object[]) dexElementsField.get(dexPathListObj);//4、獲得pathList對(duì)象中 dexElements 的屬性值
...
}
}
其實(shí) 熱修復(fù)的核心技術(shù),就一句話,
HookClassLoader
,但是要深入了解它,需要相當(dāng)多的基礎(chǔ)知識(shí),下面列舉出必須要知道的一些東西。基礎(chǔ)知識(shí)預(yù)備
1.Dex文件是什么?
我們寫安卓,目前還是用 java比較多,就算是用 kotlin,它最終也是要轉(zhuǎn)換成 java來運(yùn)行。 java文件,被編譯成 class之后,多個(gè) class文件,會(huì)被打包成
classes.dex
,被放到apk
中,安卓設(shè)備拿到apk
,去安裝解析( 預(yù)編譯balabala...),當(dāng)我們運(yùn)行 app時(shí), app的程序邏輯全都是在classes.dex
中。所以,dex
文件是什么?一句話,dex
文件是 android app的源代碼的最終打包
androidStudio
打包 apk
的時(shí)候會(huì)生成 Dex
,其實(shí)它使用的是 SDK
的 dx命令,我們可以用 dx命令自己去打包想要打包的 class. 命令格式為:dx --dex --output=output.dex xxxx.class
將上面的output 和 xxxx換成你想要的文件名即可。
注:dx.bat在 安卓 SDK的目錄下:比如我d的`C:\XXXXX\AndroidStudioAbout\sdk1\build-tools\28.0.3\dx.bat
ClassLoader
是什么?ClassLoader
來自 jdk
,翻譯為 :類加載器,用于將 class文件中的類,加載到內(nèi)存中,生成 class對(duì)象。只有存在了 Class對(duì)象,我們才可以創(chuàng)建我們想要的對(duì)象。 android SDK
繼承了JDK
的 classLoader
,創(chuàng)造出了新的 ClassLoader
子類。下圖表示了 android9.0-28 所有的ClassLoader
直接或者間接子類.
比較多的是 BaseDexClassLoader
, DexClassLoader
, PathClassLoader
, 其他這些,應(yīng)該是谷歌大佬 創(chuàng)造出來新的 類加載器子類吧,還沒研究過。
注: 關(guān)于 DexClassLoader
和 PathClassLoader
,網(wǎng)上資料有個(gè)誤區(qū),應(yīng)該不少人都認(rèn)為, PathClassLoader
用于加載 app內(nèi)部的 dex
文件, DexClassLoader
用于加載外部的 dex
文件,但是其實(shí)只要看一眼 這兩個(gè)類的關(guān)系,就會(huì)發(fā)現(xiàn),它們都是繼承自 BaseDexClassLoader
,他們的構(gòu)造函數(shù)內(nèi)部都會(huì)去執(zhí)行父類的構(gòu)造函數(shù)。他們只有一個(gè)差別,那就是 PathClssLoader
不用傳 optimizedDirectory
這個(gè)參數(shù),但是 DexClassLoader
必須傳。這個(gè)參數(shù)的作用是,傳入一個(gè) dex
優(yōu)化之后的存放目錄。而事實(shí)上,雖然 PathClassLoader
不要求傳這個(gè) optimizedDirectory
,但是它實(shí)際上是給了一個(gè)默認(rèn)值。emmmm............所以不要再認(rèn)為 PathClassLoader
不能加載外部的 dex
了,它只是沒有讓你傳 optimizedDirectory
而已。
另外:BootClassLoader
用于加載 AndroidFramework
層class文件( SDK中沒有這個(gè)BootClassLoader
,也是很奇怪) PathClassLoader
是用于Android應(yīng)用程序類的加載器,可以加載指定的 dex,以及 jar、 zip、 apk中的 classes.dex
。 DexClassLoader
可以加載指定的 dex
,以及 jar、 zip、 apk中的 classes.dex
。
ClassLoader
的雙親委托機(jī)制是什么?android里面 ClassLoader
的作用,是將 dex
文件中的類,加載到內(nèi)存中,生成 Class對(duì)象,供我們使用 (舉個(gè)例子:我寫了一個(gè) A類,app運(yùn)行起來之后,當(dāng)我需要new 一個(gè) A, ClassLoader
首先會(huì)幫我查找 A的 Class對(duì)象是否存在,如果存在,就直接給我 Class對(duì)象,讓我拿去 new A,如果不存在,就會(huì)出創(chuàng)建這個(gè) A的 Class對(duì)象。) 這個(gè)查找的過程,就遵循 雙親委托機(jī)制。一句話解釋 雙親委托機(jī)制:某個(gè) 類加載器在加載某個(gè) 類的時(shí)候,首先會(huì)將 這件事委托給 parent類加載器,依次遞歸,如果 parent類加載器可以完成加載,就會(huì)直接返回 Class對(duì)象。如果 parent找不到或者沒有父了,就會(huì) 自己加載。
下圖是 安卓源碼 ClassLoader.java
:
紅字注解,很容易讀懂 ClassLoader
去 load一個(gè) class的過程.
OK,現(xiàn)在可以來解讀我是如何去hook ClassLoader
的了. 解讀之前,先弄清楚,我為何 要 hookClassLoader
,為什么 hook了它之后,我的 fix.dex
就能發(fā)揮作用?先解決這個(gè)疑問,既然是 hook,那么自然要讀懂源碼,因?yàn)?hook就是在理解源碼思維的前提下,更改源碼邏輯。 一張圖解決你的疑問:
按照上面圖,去追蹤源碼,會(huì)發(fā)現(xiàn), ClassLoader
最終會(huì)從 DexFile
對(duì)象中去獲得一個(gè) Class對(duì)象。并且在 DexPathList
類中 findClass
的時(shí)候,存在一個(gè) Element數(shù)組的遍歷。這就意味著,如果存在多個(gè) dex
文件,多個(gè) dex
文件中都存在同樣一個(gè) class,那么它會(huì)從第一個(gè)開始找,如果找到了,就會(huì)立即返回。如果沒找到,就往下一個(gè)dex
去找。
也就是說,如果我們可以在 這個(gè)數(shù)組中插入我們自己的修復(fù)bug的 fix.dex
,那我們就可以讓我們 已經(jīng)修復(fù)bug的補(bǔ)丁類發(fā)揮作用,讓類加載器優(yōu)先讀取我們的 補(bǔ)丁類.
OK,理解了源碼的邏輯,那我們可以動(dòng)手了。來解析SDK 23的 hookClassLoader
過程吧!
確定思路,我們要改變app啟動(dòng)之后,自帶的ClassLoader對(duì)象(具體實(shí)現(xiàn)類是PathClassLoader )中 DexPathList 中 Element[] element 的實(shí)際值。
那么,步驟:
1.取得
PathClassLoader
的pathList
的屬性
2.取得PathClassLoader
的pathList
的屬性真實(shí)值(得到一個(gè)DexPathList
對(duì)象)
3.獲得DexPathList
中的dexElements
屬性
4.獲得DexPathList
對(duì)象中dexElements
屬性的真實(shí)值(它是一個(gè)Element數(shù)組) 做完這4個(gè)步驟,我們得到下面的代碼
5.用外部傳入的Dex
文件路徑,構(gòu)建一個(gè)我們自己的Element數(shù)組
6.將從外部傳入的ClassLoader
中得到的Element數(shù)組和 我們自己的Element數(shù)組合并起來, 注意,我們自己的數(shù)組元素要放前面!
7.將剛才合并的新Element數(shù)組,設(shè)置到 外部傳入的ClassLoader里面去。
OK,收官!
上面的內(nèi)容,讀起來可能會(huì)有一些疑問,我預(yù)估到了一些,將答案寫在下面
1. 當(dāng)我們需要反射獲得一個(gè)類的某個(gè)方法或者成員變量時(shí),我們只想拿
getDeclareXX
,因?yàn)槲覀冎幌肽帽绢愔械某蓡T,但是僅僅getDeclareXX
不能跨越繼承關(guān)系 拿到 父類中的非私有成員,所以我寫了ReflectionUtil.java
,支持跨越繼承關(guān)系 拿到父類的非私有成員。
2. 這種熱修復(fù),是不是下載的包會(huì)很大,和原先的apk
差不多大?答案是,NO,我們只需要將我們修復(fù)bug之后的補(bǔ)丁dex
下載到設(shè)備,讓app重啟,去讀取這個(gè)dex
即可。補(bǔ)丁包很小,甚至只有1K.
3. 這種修復(fù)方式必須重啟么? 是的,必須重啟,當(dāng)然,存在不需要重啟就可以修復(fù)bug的方法,那種方法叫做instant run方案,本文不涉及。而,當(dāng)前這種方案叫做:MultipleDex
即,多dex
方案。
*4.** 為什么要對(duì)SDK
23 ,19,14 寫不同的hook代碼?因?yàn)?code>SDK版本的變遷,導(dǎo)致 一些類的關(guān)系,變量名,方法名,方法參數(shù)(個(gè)數(shù)和類型)都會(huì)發(fā)生變化,所以,要針對(duì)各個(gè)變遷的版本進(jìn)行兼容。
另外有需要云服務(wù)器可以了解下創(chuàng)新互聯(lián)cdcxhl.cn,海內(nèi)外云服務(wù)器15元起步,三天無理由+7*72小時(shí)售后在線,公司持有idc許可證,提供“云服務(wù)器、裸金屬服務(wù)器、高防服務(wù)器、香港服務(wù)器、美國(guó)服務(wù)器、虛擬主機(jī)、免備案服務(wù)器”等云主機(jī)租用服務(wù)以及企業(yè)上云的綜合解決方案,具有“安全穩(wěn)定、簡(jiǎn)單易用、服務(wù)可用性高、性價(jià)比高”等特點(diǎn)與優(yōu)勢(shì),專為企業(yè)上云打造定制,能夠滿足用戶豐富、多元化的應(yīng)用場(chǎng)景需求。
網(wǎng)頁名稱:AndroidMuitldex熱更新修復(fù)方案原理-創(chuàng)新互聯(lián)
標(biāo)題鏈接:http://jinyejixie.com/article42/djsjec.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供搜索引擎優(yōu)化、虛擬主機(jī)、Google、App開發(fā)、外貿(mào)建站、小程序開發(fā)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內(nèi)容