先寫一個(gè)簡(jiǎn)單的插件跑起來
1. groovy創(chuàng)建插件
-
工程中新建一個(gè)java lib類型的module,我起名testgradle,src→main下的java目錄刪掉,然后新建新的目錄,包名,如圖:
-
MyPlugin:每一個(gè)自定義的Plugin都需要實(shí)現(xiàn)Plugin<T>接口,除了給Project編寫Plugin之外,也能為其他Gradle類編寫Plugin。該接口定義了一個(gè)apply()方法,在該方法中,可以操作Project,比如向其中加入Task,定義額外的Property等。
-
test-gradle.properties:這個(gè)文件名字隨便起,就是apply plugin 后面的名稱,里面把你的Myplugin全包名帶上:
-
MyPlugin:每一個(gè)自定義的Plugin都需要實(shí)現(xiàn)Plugin<T>接口,除了給Project編寫Plugin之外,也能為其他Gradle類編寫Plugin。該接口定義了一個(gè)apply()方法,在該方法中,可以操作Project,比如向其中加入Task,定義額外的Property等。
修改module的build.gradle,本地沒有maven可以自己百度裝一個(gè)哈,不難。比如我的是:
plugins {
id 'java-library'
}
apply plugin: 'java-library'
apply plugin: 'groovy'
apply plugin: 'maven'
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation gradleApi()
implementation localGroovy()
implementation 'com.android.tools.build:transform-api:1.5.0'
implementation 'com.android.tools.build:gradle:3.0.1'
}
uploadArchives{
repositories.mavenDeployer{
// 本地倉(cāng)庫(kù)路徑
repository(url:"file:///Users/shihchieh_ma/.m2/repository/")
// 唯一標(biāo)識(shí)
pom.groupId = "shihchieh.ma.demo"
// 項(xiàng)目名稱
pom.artifactId = "testgradle"
// 版本號(hào)
pom.version = "1.0.0"
}
}
-
傳本地maven上,旁邊gradle點(diǎn)擊或者命令行都行12
-
使用
-
修改根目錄的build.gradle,就是uploadArchives中的groupId:artifactId:version組合:
-
修改根目錄的build.gradle,就是uploadArchives中的groupId:artifactId:version組合:
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
mavenLocal()
google()
mavenCentral()
}
dependencies {
classpath "com.android.tools.build:gradle:4.2.0"
classpath "shihchieh.ma.demo:testgradle:1.0.0"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
mavenLocal()
google()
mavenCentral()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
-
然后隨便一個(gè)module中引用這個(gè)插件就行,我沒再新建別的,就放app module中了:
-
rebuild或者make下都行,就能看到了:
Gadle Plugin 配合ASM正式開始
-
定一個(gè)簡(jiǎn)單的目標(biāo),我想在一個(gè)Activity的oncreate中用asm插入一個(gè)Toast。
正常代碼:
編譯后:
先簡(jiǎn)單說下大概步驟和可能遇到的問題:
步驟思路:
- 目標(biāo)比較簡(jiǎn)單,通過遍歷文件夾就可以找到MainActivity的class文件。
- 對(duì)比有Toast和沒有Toast的class文件的ASMified生成的代碼差異,找到對(duì)應(yīng)有Toast的拷貝到自己對(duì)應(yīng)實(shí)現(xiàn)的MethodVisitor的visitInsn方法中。
- 插件發(fā)布到本地驗(yàn)證測(cè)試,對(duì)應(yīng)的build的transforms目錄下jar包和class文件是否有內(nèi)容,以及運(yùn)行功能是否正產(chǎn)如預(yù)期。
可能遇到的問題:
- 插件每次修改后提交到本地maven倉(cāng)庫(kù)后,sync一下代碼,然后clean,然后在rebuild下,不然有時(shí)候會(huì)報(bào)一些錯(cuò)誤影響判斷。
-
Android Studio里的ASM Bytecode Viewer插件我這里用反正是有問題的,只能在class文件右鍵點(diǎn)著看,在java文件中右鍵直接用這個(gè)插件有問題,如下圖。我新建了倆文件用beyond compare對(duì)著看的,如果誰有啥更好的工具或插件求告知。。。我這樣寫感覺效率太低了,也沒找著啥好用的工具,不過這個(gè)插件我在IDEA上試了下就是好的,感覺可以在IDEA上寫代碼然后用這個(gè)插件,我還沒試直接用IDEA寫插件。
-
遍歷目錄和jar包后要有拷出操作,通俗的說就是在實(shí)現(xiàn)的transform這個(gè)方法里,要有內(nèi)容產(chǎn)出,產(chǎn)物傳遞吧,不然app是跑不起來的。會(huì)出現(xiàn)如下錯(cuò)誤:
Unable to instantiate appComponentFactory
java.lang.ClassNotFoundException: Didn't find class "androidx.core.app.CoreComponentFactory" on path: DexPathList[[zip file "/data/app/~~81QZSgXA9N_0MNnG9dfHDg==/com.example.as_c_demo-B8o2lngQ5IM_LEQpSLsP9w==/base.apk"],nativeLibraryDirectories=[/data/app/~~81QZSgXA9N_0MNnG9dfHDg==/com.example.as_c_demo-B8o2lngQ5IM_LEQpSLsP9w==/lib/arm64, /data/app/~~81QZSgXA9N_0MNnG9dfHDg==/com.example.as_c_demo-B8o2lngQ5IM_LEQpSLsP9w==/base.apk!/lib/arm64-v8a, /system/lib64, /system/system_ext/lib64]]
核心代碼實(shí)現(xiàn):
jar包并沒有做內(nèi)容上的修改,所以直接看遍歷目錄以及修改的相關(guān)邏輯了:
- transform中獲取對(duì)應(yīng)的
Collection<DirectoryInput> directoryInputs = transformInput.getDirectoryInputs(); -
遍歷邏輯
-
ASM插入
總結(jié)
我這邊也才看ASM一陣子,這個(gè)是結(jié)合gradle plugin寫的demo,做做筆記,感覺比aop功能更強(qiáng)大,靈活性更高,并且配合插件開發(fā),可以組裝出無侵入類型的sdk,比如埋點(diǎn)SDK使用這種開發(fā)方式應(yīng)該就能輕松做到,提供的SDK對(duì)于集成方來說可以說能做到非常友好。后續(xù)繼續(xù)深入學(xué)習(xí)一下,另外有空了我在找找有沒有啥提高效率的插件或工具,這種文件比較的方式效率太低了。
20210621 20:46 update
AdviceAdapter

大概意思是說能在方法和構(gòu)造函數(shù)先于或后于或之間中插入代碼。在每個(gè)超類構(gòu)造函數(shù)調(diào)用后被調(diào)用,對(duì)象在正確初始化之前不能使用。之間插入的需求應(yīng)該比較小,沒想到什么情況需要插入到中間,所以嘗試了在最先和最后插入的嘗試。
-
目標(biāo)在onCreate中最先和最后記錄時(shí)間戳。以記錄這個(gè)onCreate執(zhí)行所需要的時(shí)間?;谥暗拇a進(jìn)行修改。
-
MyAdviceAdapter實(shí)現(xiàn)很簡(jiǎn)單,直接上代碼
-
CLASS_PAGE_TIME_RECORD
-
對(duì)應(yīng)的HockActivityCore:
這個(gè)session只是一直普通的bean,里面變量記錄下時(shí)間戳。
-
打包提交本地maven測(cè)試,一如以前操作,查看生成的class:
- 打完收工
























