概念
編譯插樁就是在代碼編譯期間修改修改已有的代碼或者生成新的代碼。
編譯插樁技術(shù)從對代碼修改的時(shí)機(jī)上來說可以分為兩種:
Java 文件:在編譯最開始的時(shí)候介入,動態(tài)生成 Java 文件,之后編譯器將生成的 Java 文件編譯成 class 文件,像 ButterKnife、Dagger 都是通過 這種方式生成代碼的。對應(yīng)的技術(shù)主要是APT(Annotation Process Tools
字節(jié)碼文件:在生成 class 文件后介入,直接修改 class文件的字節(jié)碼,達(dá)到修改代碼的目的。常用的字節(jié)碼編程框架有AspectJ、Javassist、ASM等

什么是字節(jié)碼?
Java 字節(jié)碼是Java虛擬機(jī)執(zhí)行的一種指令格式。Java源文件經(jīng)Java編譯器后得到Java字節(jié)碼(.class)文件。Java字節(jié)碼(.class)文件可以看作是Java虛擬機(jī)的可執(zhí)行文件。這些字節(jié)碼(.class)文件擁有足夠的元數(shù)據(jù)來解析類中的所有元素:類名稱、方法、屬性以及 Java 字節(jié)碼(指令)
如何查看字節(jié)碼
這里拿AndroidStudio舉例,所有基于IDEA的IDE應(yīng)該都一樣。
在Settings -> Tools中添加External Tools,輸入Name和下面三要素,保存即可
$JDKPath$\bin\javap-c -verbose $FileClass$$OutputPath$
每次生成字節(jié)碼之前記得先
build/rebuild一下工程,不然會失敗簡單分析下字節(jié)碼構(gòu)成
- kotlin文件查看方式
Android字節(jié)碼編程
在安卓中,編譯過程是由gradle task來執(zhí)行的,Gradle1.5以后提供了transform-api可以在代碼轉(zhuǎn)化為.class文件之后再打包成dex文件之前對它進(jìn)行處理,所以我們可以自定義transform,在appcompileDebugJavaWithJavac這個(gè)gradle task之后就會走我們自定義的transform。
使用場景
-
代碼生成。除了 Dagger、ButterKnife 這些常用的注解生成框架,Protocol Buffers、數(shù)據(jù)庫 ORM 框架也都會在編譯過程生成代碼。代碼生成隔離了復(fù)雜的內(nèi)部實(shí)現(xiàn),讓開發(fā)更加簡單高效,而且也減少了手工重復(fù)的勞動量,降低了出錯的可能性。 -
代碼監(jiān)控。除了網(wǎng)絡(luò)監(jiān)控和耗電監(jiān)控,我們可以利用編譯插樁技術(shù)實(shí)現(xiàn)各種各樣的性能監(jiān)控。為什么不直接在源碼中實(shí)現(xiàn)監(jiān)控功能呢?首先我們不一定有第三方 SDK 的源碼,其次某些調(diào)用點(diǎn)可能會非常分散,例如想監(jiān)控代碼中所有 new Thread() 調(diào)用,通過源碼的方式并不那么容易實(shí)現(xiàn)。 -
代碼修改。我們在這個(gè)場景擁有無限的發(fā)揮空間,例如某些第三方 SDK 庫沒有源碼,我們可以給它內(nèi)部的一個(gè)崩潰函數(shù)增加 try catch,或者說替換它的圖片庫等。我們也可以通過代碼修改實(shí)現(xiàn)無痕埋點(diǎn)。 -
代碼分析。上一期我講到持續(xù)集成,里面的自定義代碼檢查就可以使用編譯插樁技術(shù)實(shí)現(xiàn)。例如檢查代碼中的 new Thread() 調(diào)用、檢查代碼中的一些敏感權(quán)限使用等。事實(shí)上,F(xiàn)indbugs 這些第三方的代碼檢查工具也同樣使用的是編譯插樁技術(shù)實(shí)現(xiàn)。
插樁實(shí)踐
Javassist+gradle transform+annotation 實(shí)現(xiàn)方法耗時(shí)統(tǒng)計(jì)