前言:上一篇我們簡單介紹了下字節(jié)碼插樁過程,這里主要介紹如何通過一行注解來實現(xiàn)統(tǒng)計方法耗時。
在做app啟動耗時優(yōu)化
的時候迁霎,我們是有這個需求的,雖然可以通過profile來查看唯卖。
javassit
介紹:Javassist是一個動態(tài)類庫眠寿,可以用來檢查、”動態(tài)”修改以及創(chuàng)建 Java類山憨。其功能與jdk自帶的反射功能類似查乒,但比反射功能更強(qiáng)大。
常用類:
ClassPool:javassist的類池郁竟,使用ClassPool類可以跟蹤和控制所操作的類,它的工作方式與 JVM類裝載器非常相似玛迄,
CtClass: CtClass提供了檢查類數(shù)據(jù)(如字段和方法)以及在類中添加新字段、方法和構(gòu)造函數(shù)棚亩、以及改變類蓖议、父類和接口的方法虏杰。不過Javassist 并未提供刪除類中字段、方法或者構(gòu)造函數(shù)的任何方法勒虾。
CtField:用來訪問域
CtMethod :用來訪問方法
CtConstructor:用來訪問構(gòu)造器
gradle transform
getName:用于指明本Transform的名字纺阔,這個 name 并不是最終的名字,在TransformManager 中會對名字再處理
getInputTypes:用于指明Transform的輸入類型修然,可以作為輸入過濾的手段
–CLASSES表示要處理編譯后的字節(jié)碼州弟,可能是 jar 包也可能是目錄
–RESOURCES表示處理標(biāo)準(zhǔn)的 java 資源
getScopes:用于指明Transform的作用域
–PROJECT 只處理當(dāng)前項目
–SUB_PROJECTS 只處理子項目
–PROJECT_LOCAL_DEPS 只處理當(dāng)前項目的本地依賴,例如jar, aar
–EXTERNAL_LIBRARIES 只處理外部的依賴庫
–PROVIDED_ONLY 只處理本地或遠(yuǎn)程以provided形式引入的依賴庫
–TESTED_CODE 只處理測試代碼
isIncremental:用于指明是否是增量構(gòu)建。
transform:核心方法低零,用于自定義處理,在這個方法中我們可以拿到要處理的.class文件路徑婆翔、jar包路徑、輸出文件路徑等掏婶,拿到文件之后就可以對他們進(jìn)行操作
inputs.each { TransformInput input ->
input.directoryInputs.each { DirectoryInput directoryInput ->
if (directoryInput.file.isDirectory()) {
println "==== directoryInput.file = " + directoryInput.file
directoryInput.file.eachFileRecurse { File file ->
// ...對目錄進(jìn)行插入字節(jié)碼
}
}
//處理完輸入文件之后啃奴,要把輸出給下一個任務(wù)
def dest = outputProvider.getContentLocation(directoryInput.name, directoryInput.contentTypes, directoryInput.scopes, Format.DIRECTORY)
FileUtils.copyDirectory(directoryInput.file, dest)
}
input.jarInputs.each { JarInput jarInput ->
println "------=== jarInput.file === " + jarInput.file.getAbsolutePath()
File tempFile = null
if (jarInput.file.getAbsolutePath().endsWith(".jar")) {
// ...對jar進(jìn)行插入字節(jié)碼
}
/**
* 重名輸出文件,因為可能同名,會覆蓋
*/
def jarName = jarInput.name
def md5Name = DigestUtils.md5Hex(jarInput.file.getAbsolutePath())
if (jarName.endsWith(".jar")) {
jarName = jarName.substring(0, jarName.length() - 4)
}
//處理jar進(jìn)行字節(jié)碼注入處理
def dest = outputProvider.getContentLocation(jarName + md5Name, jarInput.contentTypes, jarInput.scopes, Format.JAR)
FileUtils.copyFile(jarInput.file, dest)
}
}
實現(xiàn)步驟
1. 注解類
2. gradle插件去注冊transform
gradle插件的使用請移步 gradle插件
3. transform 操作文件的實現(xiàn)步驟
4. 拿到文件后使用javassit操作
5. 將gradle插件 和 注解lib 上傳maven 供app依賴引用
gradle插件上傳maven
Android lib 上傳maven
6. 重新rebuild一下
可見class文件中test方法已經(jīng)被插入了我們想要的代碼
7. run下看看結(jié)果
后記
這里只是提供了一種啟動優(yōu)化做耗時檢測的場景,還有可以根據(jù)注解去做無痕埋點雄妥,用戶的一些主流程操作鏈路分析等等最蕾。