原文地址
Instant Run: How Does it Work?!
Instant Run是Android Studio上可以稱之為魔法的一個黑科技野来,在代碼更改時能大大的減少你重新構(gòu)建部署的時間晰搀。之所以稱之為黑科技却舀,是因為它的表現(xiàn)實在是太好了冀惭,當(dāng)我們第一次點擊run或者debug的時候,可能在時間上和不開啟沒有區(qū)別佃乘,但是當(dāng)我修改代碼后重新run的時候份蝴,速度之快可能在我來不及看運行設(shè)備的時候已經(jīng)成功的部署完畢。
以一個簡單的構(gòu)建循環(huán)流程圖開始
Instant Run的目標(biāo)十分的簡單:
盡可能多的移除中間的步驟剔交,剩下的步驟越快越好
在實際使用中意味著:
- 構(gòu)建和部署只基于增量改變
- 不重裝app
- 不重啟app
- 甚至不重啟 Activity
Hot, Warm, and Cold Swaps三種概念
Hot swap:代碼的增量改變不需要重啟一個app甚至是當(dāng)前的activity就能成功生效,在大部分方法實現(xiàn)的更改上采用的是這種方式
Warm Swap: 在修改可以被生效和看到之前這個當(dāng)前Activity需要重新啟動改衩,一般是在資源的改動上會采取這種方式
Cold Swap: 這個App會重新啟動岖常,不是重新安裝,一般是在結(jié)構(gòu)的改變時采取葫督,比如一個類的繼承接口或者簽名改變
當(dāng)點擊Run或者Debug時竭鞍,步驟是這樣的
所有的
Manifests
文件會隨著應(yīng)用程序的資源文件被合并并且打包板惑,同樣的,你的java源碼文件會被編譯成字節(jié)碼然后通過dex過程轉(zhuǎn)化為 .dex
文件也一起打包到APK中偎快。
開啟Instant Run功能第一次點擊Run或者Debug冯乘,Gradle增加了一些額外的tasks
Bytecode instrumentation(譯者注:改變編譯器生成的類的字節(jié)碼)被添加到了
.class
文件中而且一個新的 App Server被注入到app中。除此之外晒夹,一個新定義的Application類也被加進去裆馒,它注入了一個自定義的classLoader(類加載器)并且會啟動前面的App Server。因此丐怯,你的
manifest
文件被修改為使用這個新的Application喷好,如果你已經(jīng)創(chuàng)建了自己的Application類,那么Instant Run會實現(xiàn)一個代理读跷。Instant Run啟動之后梗搅,如果你修改的代碼并且重新點擊了run或者debug,Instant Run會盡可能的選擇一種過程較少的構(gòu)建過程效览,當(dāng)然根據(jù)修改的影響程度不同會有三種不同方式无切,hot swap(熱拔插),warm swap(溫拔插)以及cold swap(冷拔插)丐枉。
在Instant Run使用之前哆键,Android Studio會檢測是否存在一個正在運行的App Server并且可以支持Instant Run的socket連接,這也同時能確保這個app正運行在前臺瘦锹,Android Studio能正常工作籍嘹。
熱拔插
如圖所示,Android Studio monitors會 只 針對改變的文件運行一個自定義的Gradle task來生成一個 .dex
文件沼本,Android Studio會提取這些 .dex
文件并且將他們部署到運行的App Server中噩峦。
因為原始版本的類都已經(jīng)存在運行程序中锭沟,Gradle會轉(zhuǎn)化 這些更新的類并且使它們有效的覆蓋那些已經(jīng)存在的類抽兆。這些轉(zhuǎn)化更新的類會被App Server中的自定義類加載器來加載。
就像下面這張圖顯示的一樣
transformed的說明已經(jīng)引用摘抄在下面
Starting with 1.5.0-beta1, the Gradle plugin includes a Transform API allowing 3rd party plugins to manipulate compiled class files before they are converted to dex files.(The API existed in 1.4.0-beta2 but it's been completely revamped in 1.5.0-beta1)
The goal of this API is to simplify injecting custom class manipulations without having to deal with tasks, and to offer more flexibility on what is manipulated. The internal code processing (jacoco, progard, multi-dex) have all moved to this new mechanism already in 1.5.0-beta1.
Note: this applies only to the javac/dx code path. Jack does not use this API at the moment.
The API doc is here.
To insert a transform into a build, you simply create a new class implementing one of the Transform
interfaces, and register it with android.registerTransform(theTransform)
or android.registerTransform(theTransform, dependencies).
Important notes:
- The Dex class is gone. You cannot access it anymore through the variant API (the getter is still there for now but will throw an exception)
- Transform can only be registered globally which applies them to all the variants. We'll improve this shortly.
- There's no way to control ordering of the transforms.
We're looking for feedback on the API. Please file bugs or email us on our adt-dev mailing list.
從現(xiàn)在開始族淮,每一次應(yīng)用內(nèi)方法的調(diào)用辫红,都會被注入進來的instrumentation和App Server來監(jiān)聽這個方法是否已經(jīng)更新,如果已經(jīng)更新祝辣,調(diào)用會被代理到新“重寫”的類上贴妻,然后新的版本的方法會替代以前老的方法。
看上面這個動態(tài)圖中的操作蝙斜,在修改了
enableLocationUpdates
中的字段后名惩,可以看到黃色圈圈中我們的MainActivity的后面加上了一個 $override
的字樣。修改一個方法的實現(xiàn)能正常的通過熱拔插來正常的工作孕荠,但是如果是修改了在activity開始創(chuàng)建中的內(nèi)容呢娩鹉?
溫拔插
溫拔插需要重啟整個Activity攻谁。資源在Activity啟動的時候被加載進來,所以當(dāng)修改他們的時候需要重新啟動Activity來強制資源重新加載弯予。
現(xiàn)在任意資源文件的修改都會導(dǎo)致所有的資源被重新打包發(fā)送給App戚宦,但是Instant Run通過提供一個增量包,用來打包和部署新的或者已經(jīng)修改的資源文件锈嫩。
注意:這個溫拔插在修改Manifest中的資源文件引用的時候不能生效受楼,因為資源文件的值在APK被安裝的時候就已經(jīng)讀取了,當(dāng)我們改變Manifest文件或者Manifest引用資源的時候呼寸,Android Studio還是會執(zhí)行全部文件的編譯和部署艳汽。
不幸的是,重啟Activity也就是溫拔插的方式等舔,并不適用于結(jié)構(gòu)的改變骚灸,比如添加,刪除或者改變annotations慌植,fields甚牲,static和方法實例甚至是改變父類或是靜態(tài)初始化都會導(dǎo)致第三個階段,冷拔插蝶柿。
冷拔插
當(dāng)應(yīng)用部署的時候丈钙,你的app和子項目會被分割成10個切片,每一個都有自己的dex文件交汤,所有的類根據(jù)他們自己的包名被分配到不同的切片中雏赦,當(dāng)進入冷拔插狀態(tài)時,如果一個類被改變了芙扎,那么在重新部署的時候同一切片下的所有的類都需要重新打包成dex文件星岗。
這個方法依賴于Android在運行時加載多個dex文件的功能,這個功能在ART虛擬機上被提出來戒洼,這個ART虛擬機雖然在4.4的版本中就已經(jīng)被加入俏橘,但是實際上在5.0開始才真正的取代了Dalvik虛擬機,所以在5.0以下的設(shè)備圈浇,Android Studio采取的是全部編譯部署的形式寥掐。
Instant Run的小瑕疵
雖然Instant Run已經(jīng)足夠聰明,能根據(jù)不同的場景來采用不同的方式構(gòu)建磷蜀,但是有時候代碼修改后雖然采取了熱拔插的方式召耘,但是一些只有在application第一次啟動時初始化的操作并不能影響到,比如下面這個例子褐隆,雖然我們已經(jīng)修改了里面的值污它,但是通過Instant Run啟動的數(shù)據(jù)并沒有變化,這種情況就需要重新啟動app來讓它生效。
Instant Run提示和小技巧
Instant Run完全由Android Studio來控制衫贬,所以在debug時只通過IDE上的start / restart來啟動蜜宪,最好不要直接通過設(shè)備來啟動,可能
更詳細的小技巧清單tips and tricks祥山,這都是Android官方的文檔但是只有少量有價值的東西
- 調(diào)整你分配給Gradle進程的資源圃验。在
gradle.properties
文件中合理的設(shè)置jvmargs
參數(shù)會顯著的提高編譯的速度,無論是Instant Run還是全部編譯缝呕。你可以進行試驗并觀察構(gòu)建時間的影響澳窑,就能發(fā)現(xiàn)這個的價值。 - 因為ART虛擬機從21版本才開始正式啟用供常,在debug調(diào)試的時候?qū)?
minSdkVersion
設(shè)為21或者更高會充分發(fā)揮Instant Run的編譯速度摊聋。 - 牢記改變manifest文件會導(dǎo)致全部的編譯和部署,速度會很慢栈暇,所以麻裁,如果你的構(gòu)建過程會自動的更新manifest的任何部分,比如會自動的改變versionCode和versionName源祈,在debug時最好關(guān)掉這個行為煎源。
- Instant Run現(xiàn)在只作用于主進程,所以如果你的應(yīng)用使用多進程香缺,熱拔插和溫拔插在其他的進程會退化成冷拔插的操作甚至是整體全部的編譯如果target API在21以下手销。
- 如果在Windows下開發(fā),Windows Defender Real-Time Protection可能會導(dǎo)致Instant Run掛掉图张,可以通過添加白名單列表的方式來解決锋拖。
- 截至該文之前,暫時不支持Jack compiler祸轮,Instrumentation Tests兽埃,或者同時部署到多臺設(shè)備上。