InstantRun框架原始碼分析之二
4, onCreate
BootstrapApplication的onCreate方法如下,
public void onCreate() { if (!AppInfo.usingApkSplits) { MonkeyPatcher.monkeyPatchApplication(this, this, this.realApplication, this.externalResourcePath); MonkeyPatcher.monkeyPatchExistingResources(this, this.externalResourcePath, null); } else { MonkeyPatcher.monkeyPatchApplication(this, this, this.realApplication, null); } super.onCreate(); if (AppInfo.applicationId != null) { try { boolean foundPackage = false; int pid = Process.myPid(); ActivityManager manager = (ActivityManager) getSystemService("activity"); List<ActivityManager.RunningAppProcessInfo> processes = manager .getRunningAppProcesses(); boolean startServer = false; if ((processes != null) && (processes.size() > 1)) { for (ActivityManager.RunningAppProcessInfo processInfo : processes) { if (AppInfo.applicationId .equals(processInfo.processName)) { foundPackage = true; if (processInfo.pid == pid) { startServer = true; break; } } } if ((!startServer) && (!foundPackage)) { startServer = true; if (Log.isLoggable("InstantRun", 2)) { Log.v("InstantRun", "Multiprocess but didn't find process with package: starting server anyway"); } } } else { startServer = true; } if (startServer) { Server.create(AppInfo.applicationId, this); } } catch (Throwable t) { if (Log.isLoggable("InstantRun", 2)) { Log.v("InstantRun", "Failed during multi process check", t); } Server.create(AppInfo.applicationId, this); } } if (this.realApplication != null) { this.realApplication.onCreate(); } }
依次呼叫MonkeyPatcher的monkeyPatchApplication/ monkeyPatchExistingResources和Server的create方法,
然後利用反射呼叫realApplication也就是業務程式碼Application類的onCreate方法。
monkeyPatchApplication方法主要邏輯如下,
1.替換ActivityThread的變數mInitialApplication為realApplication
2.替換ActivityThread的變數mAllApplications 中所有的Application為realApplication。
3.替換ActivityThread的變數mPackages,mResourcePackages中的mLoaderApk中的application為realApplication。
反正一句話,替換ActivityThread中和Application有關的所有變數,並且將對應的資原始檔resource.ap_也替換。
monkeyPatchExistingResources方法邏輯如下,
1.如果resource.ap_檔案有改變,那麼新建一個AssetManager物件newAssetManager,
然後用newAssetManager物件替換所有當前Resource、Resource.Theme的mAssets成員變數。
2.如果當前的已經有Activity啟動了,還需要替換所有Activity中mAssets成員變數。
5, Server
Server主要負責熱部署、溫部署和冷部署。呼叫的流程圖如下,
Server的create方法如下,
public static void create(String packageName, Application application) {
new Server(packageName, application);
}
直接呼叫Server的構造方法。
內部類SocketServerReplyThread的handle方法從執行緒中讀取資料之後,進行簡單的校驗。
1,如果讀到7,則表示已經讀到檔案的末尾,退出讀取操作
2,如果讀到2,則表示獲取當前Activity活躍狀態,並且進行記錄
3,如果讀到3,讀取UTF-8字串路徑,讀取該路徑下檔案長度,並且進行記錄
4,如果讀到4,讀取UTF-8字串路徑,獲取該路徑下檔案MD5值,如果沒有,則記錄0,否則記錄MD5值和長度。
5,如果讀到5,先校驗輸入的值是否正確(根據token來判斷),如果正確,則在UI執行緒重啟Activity
6,如果讀到1,先校驗輸入的值是否正確(根據token來判斷),如果正確,獲取程式碼變化的ApplicationPatch列表,
首先呼叫Server的handlePatches方法進行處理,然後呼叫Server的restart方法進行重啟
7,如果讀到6,讀取UTF-8字串,showToast
InstantRun內部使用了Socket來進行通訊。也就是說當我們修改完程式點選run之後,
AndroidStudio會通過socket將資料傳遞給我們,最終呼叫的是handlePatches方法。
Server的handlePatches方法如下,
private int handlePatches(List<ApplicationPatch> changes,
boolean hasResources, int updateMode) {
if (hasResources) {
FileManager.startUpdate();
}
for (ApplicationPatch change : changes) {
String path = change.getPath();
if (path.endsWith(".dex")) {
handleColdSwapPatch(change);
boolean canHotSwap = false;
for (ApplicationPatch c : changes) {
if (c.getPath().equals("classes.dex.3")) {
canHotSwap = true;
break;
}
}
if (!canHotSwap) {
updateMode = 3;
}
} else if (path.equals("classes.dex.3")) {
updateMode = handleHotSwapPatch(updateMode, change);
} else if (isResourcePath(path)) {
updateMode = handleResourcePatch(updateMode, change, path);
}
}
if (hasResources) {
FileManager.finishUpdate(true);
}
return updateMode;
}
根據ApplicationPatch列表中逐個取出改變的ApplicationPatch檔案,
1.如果字尾為“.dex”,呼叫 handleColdSwapPatch方法進行冷部署處理
2.如果字尾為“classes.dex.3”,呼叫 handleHotSwapPatch方法進行熱部署處理
3.其他情況,溫部署,呼叫 handleResourcePatch方法處理資源
5.1 Cold Swap
Server 的handleColdSwapPatch方法如下,
private static void handleColdSwapPatch(ApplicationPatch patch) {
if (patch.path.startsWith("slice-")) {
File file = FileManager.writeDexShard(patch.getBytes(), patch.path);
if (Log.isLoggable("InstantRun", 2)) {
Log.v("InstantRun", "Received dex shard " + file);
}
}
}
把dex檔案寫到私有目錄,等待整個app重啟,重啟之後,使用IncrementalClassLoader載入dex。
5.2 Hop Swap
在Server 的handleHotSwapPatch方法中,
首先將patch的dex檔案寫入到臨時目錄,然後使用DexClassLoader去載入dex。AppPatchesLoaderImpl是編譯生成的,在這裡可以看到修改的類等資訊。
然後利用反射呼叫AppPatchesLoaderImpl類的load方法,實際上是呼叫父類AbstractPatchesLoaderImpl的load方法。load方法如下,
public boolean load() {
try {
for (String className : getPatchedClasses()) {
ClassLoader cl = getClass().getClassLoader();
Class<?> aClass = cl.loadClass(className + "$override");
Object o = aClass.newInstance();
Class<?> originalClass = cl.loadClass(className);
Field changeField = originalClass.getDeclaredField("$change");
changeField.setAccessible(true);
Object previous = changeField.get(null);
if (previous != null) {
Field isObsolete = previous.getClass().getDeclaredField(
"$obsolete");
if (isObsolete != null) {
isObsolete.set(null, Boolean.valueOf(true));
}
}
changeField.set(null, o);
if ((Log.logging != null)
&& (Log.logging.isLoggable(Level.FINE))) {
Log.logging.log(Level.FINE, String.format("patched %s",
new Object[] { className }));
}
}
} catch (Exception e) {
if (Log.logging != null) {
Log.logging.log(Level.SEVERE, String.format(
"Exception while patching %s",
new Object[] { "foo.bar" }), e);
}
return false;
}
return true;
}
載入class名稱+override類,給$change賦值,這就是Instance Run的關鍵, $change又是什麼意思呢?
在執行程式的時候,就可以根據該變數,執行被替換的函式。
5.3 Warm Swap
Server 的handleResourcePatch方法如下,
private static int handleResourcePatch(int updateMode,
ApplicationPatch patch, String path) {
if (Log.isLoggable("InstantRun", 2)) {
Log.v("InstantRun", "Received resource changes (" + path + ")");
}
FileManager.writeAaptResources(path, patch.getBytes());
updateMode = Math.max(updateMode, 2);
return updateMode;
}
將資源的patch寫入到私有目錄,等到restart之後生效. 可以看到獲取了對應的資原始檔,
就是/data/data/[applicationId]/files/instant-run/resources.ap_,InstantRun直接對它進行了位元組碼操作,
把通過Socket傳過來的修改過的資源傳遞了進去。
最後,Server的restart方法根據不同的InstantRun的updateMode模式,進行重啟,使上述的3中部署模式生效。
6,總結
第一次編譯apk:
1.把Instant-Run.jar和instant-Run-bootstrap.jar打包到主dex中
2.替換AndroidManifest.xml中的application配置
3.使用asm工具,在每個類中新增$change,在每個方法前加邏輯
4.把原始碼編譯成dex,然後存放到壓縮包instant-run.zip中
app執行期:
1.獲取更改後資源resource.ap_的路徑
2.設定ClassLoader。setupClassLoader:
使用IncrementalClassLoader載入apk的程式碼,將原有的BootClassLoader → PathClassLoader改為BootClassLoader
→ IncrementalClassLoader → PathClassLoader繼承關係。
3.createRealApplication:
建立apk真實的application
4.monkeyPatchApplication
反射替換ActivityThread中的各種Application成員變數
5.monkeyPatchExistingResource
反射替換所有存在的AssetManager物件
6.呼叫realApplication的onCreate方法
7.啟動Server,Socket接收patch列表
有程式碼修改時
1.生成對應的$override類
2.生成AppPatchesLoaderImpl類,記錄修改的類列表
3.打包成patch,通過socket傳遞給app
4.app的server接收到patch之後,分別按照handleColdSwapPatch、handleHotSwapPatch、handleResourcePatch等待對patch進行處理
5.restart使patch生效
InstantRun利用了transform api去生成位元組碼,這樣的方式不靈活,因為所有的transform操作是由TransformManager管理的,
也就是說它執行的時機是固定的,如果涉及到混淆,dex等操作,這些task的順序都是不可變的,這樣的就會出錯。
相關推薦
InstantRun框架原始碼分析之二
4, onCreate BootstrapApplication的onCreate方法如下, public void onCreate() { if (!AppInfo.usingApkSplits) { MonkeyPatcher.monkeyPatchApp
Hadoop原始碼分析之二(RPC機制之Call處理)
下面介紹在整個處理機制中怎麼把具體的Request Call轉換並呼叫到整體的實現邏輯。 主要以NameNode Client PRC Server作為例子來說明,整個轉換通過Google Protocol Buffer RPC來實現。 fina
spark mllib原始碼分析之二分類邏輯迴歸evaluation
在邏輯迴歸分類中,我們評價分類器好壞的主要指標有精準率(precision),召回率(recall),F-measure,AUC等,其中最常用的是AUC,它可以綜合評價分類器效能,其他的指標主要偏重一些方面。我們介紹下spark中實現的這些評價指標,便於使用sp
zookeeper原始碼分析之二客戶端啟動
ZooKeeper Client Library提供了豐富直觀的API供使用者程式使用,下面是一些常用的API: create(path, data, flags): 建立一個ZNode, path是其路徑,data是要儲存在該ZNode上的資料,flags常用的有: PERSISTEN, PERSIS
Spark RPC 框架原始碼分析(二)執行時序
前情提要: Spark RPC 框架原始碼分析(一)簡述 一. Spark RPC 概述概述 上一篇我們已經說明了 Spark RPC 框架的一個簡單例子,以及一些基本概念的說明。這一篇我們主要講述其執行時序,從而揭露 Spark RPC 框架的執行原理。我們將分為兩部分,分別從服務端和客戶端
DDPush開源推送框架原始碼分析之Client到DDPush(UDP模式)
在前一篇文章中我們主要分析了AppServer是如何連線到DDPush,並向DDPush推送訊息,還沒有看過的朋友請移步DDPush開源推送框架原始碼分析之APPServer到DDPush。 本篇文章主要講解Client(客戶端)如何連線到DDPush,並向DDPush傳送
spark 原始碼分析之二十一 -- Task的執行流程
引言 在上兩篇文章 spark 原始碼分析之十九 -- DAG的生成和Stage的劃分 和 spark 原始碼分析之二十 -- Stage的提交 中剖析了Spark的DAG的生成,Stage的劃分以及Stage轉換為TaskSet後的提交。 如下圖,我們在前兩篇文章中剖析了D
spark 原始碼分析之二十二-- Task的記憶體管理
問題的提出 本篇文章將回答如下問題: 1. spark任務在執行的時候,其記憶體是如何管理的? 2. 堆內記憶體的定址是如何設計的?是如何避免由於JVM的GC的存在引起的記憶體地址變化的?其內部的記憶體快取池回收機制是如何設計的? 3. 堆外和堆內記憶體分別是通過什麼來分配的?其資料的偏移
Android框架原始碼解析之(二)OKhttp
原始碼在:https://github.com/square/okhttp 包實在是太多了,OKhttp核心在這塊https://github.com/square/okhttp/tree/master/okhttp 直接匯入Android Studio中即可。 基本使用:
Glide原始碼分析(二)——從用法來看之load&into方法
上一篇,我們分析了with方法,文章連結: https://blog.csdn.net/qq_36391075/article/details/82833260 在with方法中,進行了Glide的初始化,建立了RequesManger,並且綁定了生命週期,最終返回了一個Reques
zigbee 之ZStack-2.5.1a原始碼分析(二) 無線接收控制LED
本文描述ZStack-2.5.1a 模板及無線接收移植相關內容。 main HAL_BOARD_INIT // HAL_TURN_OFF_LED1 InitBoard HalDriverInit HalAdcInit
【NLP】【二】jieba原始碼分析之分詞
【一】詞典載入 利用jieba進行分詞時,jieba會自動載入詞典,這裡jieba使用python中的字典資料結構進行字典資料的儲存,其中key為word,value為frequency即詞頻。 1. jieba中的詞典如下: jieba/dict.txt X光 3 n X光線 3
tornado原始碼分析(二)之iostream
在事件驅動模型中,所有任務都是以某個事件的回撥函式的方式新增至事件迴圈中的,如:HTTPServer要從socket中讀取客戶端傳送的request訊息,就必須將該socket新增至ioloop中,並設定回掉函式,在回掉函式中從socket中讀取資料,並且檢查request訊息是否全部接收到了,如果
原始碼分析之基於LinkedList手寫HahMap(二)
package com.mayikt.extLinkedListHashMap; import java.util.LinkedList; import java.util.concurrent.ConcurrentHashMap; /** * 基於linkedList實現hashMap *
Spark core原始碼分析之spark叢集的啟動(二)
2.2 Worker的啟動 org.apache.spark.deploy.worker 1 從Worker的伴生物件的main方法進入 在main方法中首先是得到一個SparkConf例項conf,然後將conf和啟動Worker傳入的引數封裝得到Wor
HBase原始碼分析之HRegion上compact流程分析(二)
2016年03月03日 21:38:04 辰辰爸的技術部落格 閱讀數:2767 版權宣告:本文為博主原創文章,未經博主允許不得轉載。 https://blog.csdn.net/lipeng_bigdata/article/details/50791205
JDK1.8集合框架原始碼分析二-------------LinkedList
0.LinkedList特點 0.1) 刪除或新增效率高:不會移動資料元素,只會維護內部節點的關係 0.2) 查詢慢: 在其內部沒有下標,也即沒有索引,需要使用for迴圈遍歷,LInkedList為了提高效率,
ArrayList集合(JDK1.8) 【集合框架】JDK1.8原始碼分析之ArrayList(六)
簡述 List是繼承於Collection介面,除了Collection通用的方法以外,擴充套件了部分只屬於List的方法。 常用子類 ?ArrayList介紹 1.資料結構 其底層的資料結構是陣列,陣列元素型別為Object型別,即可以存放所
[jjzhu學java]之JDK集合框架原始碼分析
Java Collection 圖中實線邊框表示的是實現類(ArrayList, Hashtable等),虛線邊框的是抽象類(AbstractCollection,AbstractSequentialListd等),而點線邊框的是介面(Collect
以太坊原始碼分析之 P2P網路(二、節點發現流程)
區塊鏈特輯 :https://blog.csdn.net/fusan2004/article/details/80879343,歡迎查閱,原創作品,轉載請標明!上一篇文章簡單介紹了下一些基礎的型別定義,從這一篇開始我們將描述p2p網路的更多細節。從關於節點的定義來看,其實不同