背景:
1.趨勢:目前很多知名架構在後期維護中基于性能優化,代碼解耦的考慮都加入Gradle Plugin的技術,如Alibaba的ARouter, 360的Replugin等等。在後期的版本更新後,在性能上都有很大的優化。
2.需求:随着項目中對于APM(Application Performance Management) 越來越關注,後期功能上會加入性能監控,網絡請求耗時等一些監控,這個時候為了減少對原業務功能的影響,達到“無需修改原有結構,無侵入接入”和“無性能損害”的目标,就需要有一種辦法在編譯階段自動插入代碼實作(運作階段操作肯定會消耗性能,pass)。那就是傳說中的“插樁”,AOP思想(面向切面程式設計),使用Gradle Plugin 技術。
3.現狀:産品經理為了對産品上線之後使用者操作資料的統計,每次上線前會對代碼中增加埋點。随着功能的龐大和業務的複雜,造成埋點代碼越來越多,這些代碼在某種程度會影響業務邏輯的閱讀。這個時候如果能把埋點代碼和業務代碼進行分離,隻要在編譯的時候把埋點代碼注入到業務代碼就可以了。
概述
Gradle Plugin是Android項目中為了達到優化性能,代碼解耦的目标,在編譯階段通過位元組碼的改變在已存在的類中插入代碼,或者建立一個新的類,采用的是AOP面向切面思想程式設計,接下來以下幾個方面進行講述:
1.Android App的編譯過程
2.自定義Gradle Plugin的建立和Transform api的使用
3.使用asm實作位元組碼插樁
編譯過程
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1UGMwEmMidDNhRjNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
編譯過程
上圖介紹了一個app是如何編譯成在手機上運作的apk,gradle plugin 是在生成.class檔案之後,生成.dex之前對位元組碼進行操作。它其中包含.class 檔案和第三方的jar檔案,在編譯過程中周遊拿到這些檔案,然後對需要更改的類檔案進行更改。
自定義Gradle-Plugin 插件
1.自定義插件
1.首先引入jar包:
2.0.0以前:compile'com.android.tools.build:transform-api:1.5.0'
2.0.0以後://從2.0.0版本開始就是在gradle-api中
implementation'com.android.tools.build:gradle-api:3.1.4
2.建立自定義插件:
首先建立自定義插件,在自定以插件中注冊一個自定義的transform,編寫語言可以是groovy,java,kotlin
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1UGMwEmMidDNhRjNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
Arouter 建立的自定義插件
如上圖apply方法中隻有在主app中才會需要導入這個插件生成注冊的代碼。然後建立自定義RegisterTransform 注冊到插件中,其中建立了一個集合,指派給這個插件,這個會在後面講解。
2自定義transform
1.transform原理及應用
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1UGMwEmMidDNhRjNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
transform原理
1.每一個transform 是一個Gradle task,TaskMandger 會把Transform 串聯起來,第一個 Transform 會把javac編譯的class檔案,jar包,resource(asset中的資源)接收到,然後進行處理,傳送給下一個Transform。
2.自定義的transform會插到系統自帶的前面。
3.dexBuilder,dexMerger,mergeJavaRes,mergeJniLibs,proguard 這些常見Transform都屬于系統的Transform。
2.transform類詳解
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1UGMwEmMidDNhRjNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
自定義Transform
1.getName:傳回插件的名字,這個 name 并不是最終的名字,
格式如:transformClassesWith“name”ForDebug
2.getInputTypes: 擷取過濾的類型:
CONTENT_CLASS:表示要處理編譯後的位元組碼,可能是 jar 包也可能是目錄
CONTENT_RESOURCES:表示處理标準的 java 資源,在assert檔案夾中
3.getScopes:擷取過濾的範圍
PROJECT: 隻處理目前項目
SUB_PROJECTS:隻處理子項目
PROJECT_LOCAL_DEPS:隻處理目前項目的本地依賴,例如jar, aar
EXTERNAL_LIBRARIES:隻處理外部的依賴庫
PROVIDED_ONLY:隻處理本地或遠端以provided形式引入的依賴庫
TESTED_CODE:測試代碼
4 isIncremental: 目前 Transform 是否支援增量編譯
NOTCHANGED: 目前檔案不需處理,甚至複制操作都不用;
ADDED、CHANGED: 正常處理,輸出給下一個任務;
REMOVED: 移除outputProvider擷取路徑對應的檔案。
5:transform()方法的參數詳解
inputs :消費型輸入
referencedInputs:引用型輸入,無需輸出
isIncremental:是否增量更新
outputProvider:管理輸出路徑
6. transform 的優化:private WaitableExecutor waitableExecutor;
waitableExecutor =WaitableExecutor.useGlobalSharedThreadPool();
//異步并發處理jar/class
waitableExecutor.execute(()->{
bytecodeWeaver.weaveJar(srcJar,destJar);returnnull;
});
//異步并發處理jar/class
waitableExecutor.execute(()->{
bytecodeWeaver.weaveSingleClassToFile(file,outputFile,inputDirPath);returnnull;
});
//等待所有任務結束
waitableExecutor.waitForTasksWithQuickFail(true);
3.ASM使用
簡介:
ASM 可以直接産生二進制的class 檔案,也可以在增強既有類的功能。Java class 被存儲在嚴格格式定義的 .class檔案裡,這些類檔案擁有足夠的中繼資料來解析類中的所有元素:類名稱、方法、屬性以及 Java 位元組碼(指令)。
ASM架構中的核心類有以下幾個:
ClassReader:該類用來解析編譯過的class位元組碼檔案。
ClassWriter:該類用來重新建構編譯後的類,比如說修改類名、屬性以及方法,甚至可以生成新的類的位元組碼檔案。
ClassVisitor:主要負責 “拜訪” 類成員資訊。其中包括标記在類上的注解,類的構造方法,類的字段,類的方法,靜态代碼塊。
AdviceAdapter:實作了MethodVisitor接口,主要負責 “拜訪” 方法的資訊,用來進行具體的方法位元組碼操作。
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1UGMwEmMidDNhRjNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
ClassVisitor 實作
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI1UGMwEmMidDNhRjNvwFcvwVbvNmL1h2cuFWaq5yd3d3Lc9CX6MHc0RHaiojIsJye.jpg)
MethodVisitor 實作
總結:Android Gradle Plugin 大概講到這,最後一步Asm實作可以使用Android studio 插件asm-bytecode-outline,它會生成asm實作代碼。