Android 熱修復其實很簡單
一、什麼是熱修復
熱修復說白了就是”打補丁”,比如你們公司上線一個app,使用者反應有重大bug,需要緊急修復。如果按照通
常做法,那就是程式猿加班搞定bug,然後測試,重新打包併發布。這樣帶來的問題就是成本高,效率低。於是,熱
修復就應運而生.一般通過事先設定的介面從網上下載無Bug的程式碼來替換有Bug的程式碼。這樣就省事多了,用
戶體驗也好。
二、熱修復的原理
1.Android的類載入機制
Android的類載入器分為兩種,PathClassLoader和DexClassLoader,兩者都繼承自BaseDexClassLoader
PathClassLoader程式碼位於libcore\dalvik\src\main\java\dalvik\system\PathClassLoader.java
DexClassLoader程式碼位於libcore\dalvik\src\main\java\dalvik\system\DexClassLoader.java
BaseDexClassLoader程式碼位於libcore\dalvik\src\main\java\dalvik\system\BaseDexClassLoader.java
- PathClassLoader
-
用來載入系統類和應用類
-
DexClassLoader
用來載入jar、apk、dex檔案.載入jar、apk也是最終抽取裡面的Dex檔案進行載入.
2.熱修復機制
看下PathClassLoader程式碼
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);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
DexClassLoader程式碼
public class DexClassLoader extends BaseDexClassLoader {
public DexClassLoader(String dexPath, String optimizedDirectory, String libraryPath, ClassLoader parent) {
super(dexPath, new File(optimizedDirectory), libraryPath, parent);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
兩個ClassLoader就兩三行程式碼,只是呼叫了父類的建構函式.
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("Didn't find class \"" + name + "\" on path: " + pathList);
for (Throwable t : suppressedExceptions) {
cnfe.addSuppressed(t);
}
throw cnfe;
}
return c;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
在BaseDexClassLoader 建構函式中建立一個DexPathList類的例項,這個DexPathList的建構函式會建立一個dexElements 陣列
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);
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
然後BaseDexClassLoader 重寫了findClass方法,呼叫了pathList.findClass,跳到DexPathList類中.
/* 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;
}
...
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
會遍歷這個陣列,然後初始化DexFile,如果DexFile不為空那麼呼叫DexFile類的loadClassBinaryName方法返回Class例項.
歸納上面的話就是:ClassLoader會遍歷這個陣列,然後載入這個陣列中的dex檔案.
而ClassLoader在載入到正確的類之後,就不會再去載入有Bug的那個類了,我們把這個正確的類放在Dex檔案中,讓這個Dex檔案排在dexElements陣列前面即可.
這裡有個問題,可參考QQ空間團隊的 安卓App熱補丁動態修復技術介紹
概括來講:如果引用者和被引用者的類(直接引用關係)在同一個Dex時,那麼在虛擬機器啟動時,被引用類就會被打上CLASS_ISPREVERIFIED標誌,這樣被引用的類就不能進行熱修復操作了.
那麼我們就要阻止被引用類打上CLASS_ISPREVERIFIED標誌.QQ空間的方法是在所有引用到該類的建構函式中插入一段程式碼,程式碼引用到別的類.
三、熱修復的例子
我用的是阿里開源的熱修復框架AndFix熱修復框架地址
其實它的原理也是動態載入class檔案,然後呼叫反射完成修復.可參考我上一篇寫的
Java的ClassLoader載入機制
AndFix是 “Android Hot-Fix”的縮寫。它支援Android 2.3到6.0版本,並且支援arm與X86系統架構的裝置。完美支援Dalvik與ART的Runtime。AndFix 的補丁檔案是以 .apatch 結尾的檔案。
我這是用eclipse寫的Demo.
1.把AndFix抽取成library依賴的形式
2.新建一個AndFixDemo專案,依賴AndFix這個library
2.1
新建一個MyApplication繼承Application
public class MyApplication extends Application {
private static final String TAG = "MyApplication";
/**
* apatch檔案
*/
private static final String APATCH_PATH = "/Dennis.apatch";
private PatchManager mPatchManager;
@Override
public void onCreate() {
super.onCreate();
// 初始化
mPatchManager = new PatchManager(this);
mPatchManager.init("1.0"); // 版本號
// 載入 apatch
mPatchManager.loadPatch();
//apatch檔案的目錄
String patchFileString = Environment.getExternalStorageDirectory().getAbsolutePath() + APATCH_PATH;
File apatchPath = new File(patchFileString);
if (apatchPath.exists()) {
Log.i(TAG, "補丁檔案存在");
try {
//新增apatch檔案
mPatchManager.addPatch(patchFileString);
} catch (IOException e) {
Log.i(TAG, "打補丁出錯了");
e.printStackTrace();
}
} else {
Log.i(TAG, "補丁檔案不存在");
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
實際當中肯定是通過網路介面下載apatch檔案,我這裡為了方便演示就放在了SD卡根目錄
2.2
在MainActivity用一個按鈕彈出吐司,上面是有Bug的程式碼,下面是修正後的程式碼
分別打包成Bug.apk和NoBug.apk
2.3
然後要用到一個生成補丁的工具apkpatch
解壓
_MACOSX是給OSX系統用的
.bat是給window系統用的
我用得是.bat
把之前生成的Bug.apk和NoBug.apk,還有打包所使用的keystore檔案放到apkpatch-1.0.3目錄下
開啟cmd,進入到apkpatch-1.0.3目錄下,輸入如下指令
apkpatch.bat -f NoBug.apk -t Bug.apk -o Dennis -k keystore -p 111111 -a 111111 -e 111111
每個引數含義如下
-f 新版本的apk
-t 舊版本的apk
-o 輸出apatch檔案的資料夾,可以隨意命名
-k 打包的keystore檔名
-p keystore的密碼
-a keystore 使用者別名
-e keystore 使用者別名的密碼
如果出現add modified …….就表示成功了,去apkpatch-1.0.3目錄看下,新增了Dennis目錄
我把這個檔案改為Dennis.apatch
2.4
手機裝上Bug.apk執行起來
然後把Dennis.apatch 放到SD卡根目錄,退出app,再進入,按下按鈕
最後附上Demo還有apk和apatch 檔案 開啟連結
轉自:http://blog.csdn.net/qq_31530015/article/details/51785228?locationNum=11
相關文章
- Android熱修復簡單總結Android
- Promise 其實很簡單Promise
- 快排其實很簡單
- 其實泛型很簡單泛型
- 紅黑樹其實很簡單
- android 整合微信支付和支付寶支付其實很簡單Android
- Android 熱修復Android
- Redux其實很簡單(原理篇)Redux
- Flutter Android 端熱修復(熱更新)實踐FlutterAndroid
- 手動實現最簡單的Android熱修復(最新最全詳細小白教程)Android
- Android熱修復原理Android
- 高大上的詞雲,其實很簡單
- Android熱修復原理(一)熱修復框架對比和程式碼修復Android框架
- 簡單易懂的tinker熱修復原理分析
- 【Android 熱修復】美團Robust熱修復框架原理解析Android框架
- 電腦上永久刪除的檔案怎麼恢復?其實很簡單
- 回收站裡的檔案刪除了怎麼恢復,其實很簡單
- 執行緒池其實看懂了也很簡單執行緒
- 撥開雲霧見月明—計算其實很簡單
- 這樣看java記憶體模型其實很簡單Java記憶體模型
- 這是DDD建模最難的部分(其實很簡單)
- 有了它,在家遠端辦公其實可以很簡單
- 新手也能看懂,訊息佇列其實很簡單佇列
- 如何做到數字化轉型?其實方法很簡單
- robust 熱修復實踐
- 2018深入解析Android熱修復技術Android
- 月薪3000和30000的人差在哪?知道答案後其實很簡單
- 深入探索Android熱修復技術原理讀書筆記 —— 程式碼熱修復技術Android筆記
- 深入探索Android熱修復技術原理讀書筆記 —— 資源熱修復技術Android筆記
- 深入探索Android熱修復技術原理讀書筆記 —— 熱修復技術介紹Android筆記
- Android熱修復之Tinker整合最新詳解Android
- Android每週一輪子:Nvwa(熱修復)Android
- Android熱修復(Hot Fix)案例全剖析(一)Android
- Python 基礎起步 (九) 條件語句 if elif else 其實很簡單Python
- EventLoop其實如此簡單OOP
- 短影片一鍵同步系統,同步30+平臺其實很簡單
- 優思學院|精益六西格瑪很深奧?其實很簡單
- 筆記 深入探索Android熱修復技術原理筆記Android
- Android進階(八)熱修復基本原理Android