寫(xiě)在前面:
在基礎(chǔ)熟練的基礎(chǔ)上,完全可以考慮基于Booster、ByteX等框架來(lái)開(kāi)發(fā)各薇,效率應(yīng)該會(huì)高一些顽照。
修改字節(jié)碼的插件不止asm一個(gè)乏冀,還有javaassist等吉嚣,可以多做一些嘗試幔亥,按照需求選擇適合自己項(xiàng)目的琳疏。
本次分享的目的旨在展示gradle插件開(kāi)發(fā)的過(guò)程粪般、思路拼余、需要的基礎(chǔ)、遇到問(wèn)題后如何分析等亩歹,核心在于打好基礎(chǔ)匙监。
整體目標(biāo):hook應(yīng)用內(nèi)所有的手勢(shì),還原成操作手勢(shì)事件小作,交由服務(wù)端進(jìn)行軌跡還原等操作亭姥。
核心任務(wù)拆分
核心任務(wù)聚焦:拿到應(yīng)用內(nèi)所有手勢(shì)事件的MotionEvent
關(guān)鍵節(jié)點(diǎn):
事件分發(fā)-何處hook
字節(jié)碼基礎(chǔ)-如何修改
apk構(gòu)建過(guò)程-何時(shí)修改
使用gradle-如何開(kāi)發(fā)插件
MotionEvent處理:自行將原始的MotionEvent合并成為我們常用的事件序列。
事件分發(fā)-何處hook
hook點(diǎn):activity的時(shí)機(jī)比較合適
觸摸事件最終都會(huì)交由Acivity來(lái)處理
-
Acivity#dispatchTouchEvent方法負(fù)責(zé)分發(fā)給對(duì)應(yīng)的view進(jìn)行處理
- Activity -> PhoneWindow -> DecorView -> ViewGroup -> View
最終做到的如下所示:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TouchEventDispatcher.dispatchTouchEvent(MainActivity.this, ev);
return super.dispatchTouchEvent(ev);
}
適配不同版本的Activity
對(duì)特定的根Activity做處理顾稀,這樣可以對(duì)其子類(lèi)進(jìn)行處理致份,可以覆蓋所有的Activity。
AppCompatActivity:
v7_AppCompat_Activity: "android/support/v7/app/AppCompatActivity";
androidx_AppCompat_Activity: "androidx/appcompat/app/AppCompatActivity";
字節(jié)碼基礎(chǔ)-如何修改
-
字節(jié)碼本質(zhì):二進(jìn)制文件
jvm校驗(yàn)通過(guò)的就是合法的字節(jié)碼文件础拨,不問(wèn)來(lái)源氮块。
比如你通過(guò)文本編輯器寫(xiě)的字節(jié)碼文件,只要符合字節(jié)碼格式诡宗,那也是合法的字節(jié)碼文件滔蝉。
-
字節(jié)碼格式:
[擴(kuò)展]IDE借助jclasslib插件查看細(xì)節(jié):
-
jvm指令集
-
如何修改字節(jié)碼- 借助ASM等工具.
1、ClassReader:對(duì)class文件進(jìn)行讀取與解析塔沃;
2蝠引、ClassWriter:參與字節(jié)碼修改,并將修改后的字節(jié)碼內(nèi)容以字節(jié)流的形式返回蛀柴。
3螃概、使用ClassNode與MethodNode配合判斷:目標(biāo)Class中如果沒(méi)有目標(biāo)方法,就使用ClassWriter + MethodVisitor鸽疾,無(wú)中生有的增加目標(biāo)方法(dispatchTouchEvent)的默認(rèn)實(shí)現(xiàn)吊洼。
@Override public boolean dispatchTouchEvent(MotionEvent ev) { return super.dispatchTouchEvent(ev); }
- 2、ClassVisitor和MethodVisitor:找到目標(biāo)Class(CompatActivity的子類(lèi))制肮,如果存在目標(biāo)方法(dispatchTouchEvent)冒窍,則直接在方法入口處增加我們的工具方法調(diào)用。
@Override public boolean dispatchTouchEvent(MotionEvent ev) { TouchEventDispatcher.dispatchTouchEvent(MainActivity.this, ev); return super.dispatchTouchEvent(ev); }
- class會(huì)遍歷兩遍豺鼻。
apk構(gòu)建過(guò)程-何時(shí)修改
- 整體架構(gòu)圖
-
詳細(xì)的圖:
333.png
使用gradle-如何開(kāi)發(fā)插件
-
使用groovy語(yǔ)言開(kāi)發(fā)综液,面向Java平臺(tái)。也可以kotlin開(kāi)發(fā)(趨勢(shì))儒飒。
-
Project基礎(chǔ)概念
每一個(gè) build.gradle 文件都會(huì)轉(zhuǎn)換成一個(gè) Project 對(duì)象谬莹。在 Gradle 術(shù)語(yǔ)中,Project 對(duì)象對(duì)應(yīng)的是
Build Script
。加載插件其實(shí)是調(diào)用它的apply函數(shù)附帽。
Project 包含若干 Tasks埠戳。另外,由于 Project 對(duì)應(yīng)具體的工程士葫,所以需要為 Project 加載所需要的插件乞而,比如為 Java 工程加載 Java 插件送悔。其實(shí)慢显,一個(gè) Project 包含多少 Task 往往是插件決定的。
-
Task基礎(chǔ)概念:
一個(gè)Task表示構(gòu)建的單個(gè)原子工作欠啤,例如編譯類(lèi)或生成javadoc荚藻。
每個(gè)Task都屬于一個(gè)Project。
一組有依賴(lài)關(guān)系的Task洁段,組成了Project应狱。
查看gradle源碼:通過(guò)Android Studio即可
-
適配gradle版本。這里選擇支持有代表性的gradle 4.2和 gradle 7.2祠丝。
-
gradle相關(guān)資料:
Gradle API 文檔疾呻。點(diǎn)擊
INDEX
,然后搜索腳本名字即可写半。Android Gradle權(quán)威指南.pdf
Android之Gradle 深入理解.pdf
MotionEvent處理:自行將原始的MotionEvent合并成為我們常用的事件序列岸蜗。
單擊
雙擊
長(zhǎng)按
多指觸控
Cancel事件的處理
項(xiàng)目
項(xiàng)目地址
tinyvampirepudge/hook-touch-event
效果:
插件支持gradle4.2和gradle7.2
插件支持常見(jiàn)的依賴(lài)方式。module叠蝇、aar璃岳、jar等
gradle插件優(yōu)勢(shì):
對(duì)現(xiàn)有業(yè)務(wù)代碼,基本上是無(wú)侵入式的修改悔捶×蹇叮可以如果不需要,可以隨時(shí)移除蜕该。
增加字節(jié)碼的相關(guān)耗時(shí)主要是在編譯期間犁柜,非運(yùn)行時(shí)。運(yùn)行時(shí)只是增加正常的代碼調(diào)用耗時(shí)堂淡。
字節(jié)碼增加代碼之后赁温,不會(huì)影響mapping文件中的行號(hào)。即不會(huì)影響現(xiàn)有代碼的錯(cuò)誤堆棧信息淤齐。
插件兼容性
項(xiàng)目架構(gòu)圖
插件代碼
目標(biāo):修改字節(jié)碼股囊,將所有經(jīng)過(guò)Activity的MotionEvent都給我們的TouchEventDispatcher發(fā)送一份。
整體目錄:
找到目標(biāo)class
-
TouchEventTransform:遍歷并找到我們的目標(biāo)class更啄,然后修改
getInputTypes:TransformManager.CONTENT_CLASS
getScopes:[實(shí)測(cè)] TransformManager.PROJECT_ONLY 配合每個(gè)子module下都apply插件稚疹,則可以遍歷所有module下的class。
transform:遍歷class文件,找到我們的目標(biāo)class内狗。
借助asm插件中的ClassVisitor查找到目標(biāo)類(lèi)
修改目標(biāo)class
PluginUtils.genDispatchTouchEvent:class中沒(méi)有目標(biāo)方法怪嫌,增加對(duì)應(yīng)方法默認(rèn)實(shí)現(xiàn)的字節(jié)碼
借助asm插件中的MethodVisitor,在目標(biāo)方法中通過(guò)字節(jié)碼的方式添加我們的代碼
-
將字節(jié)碼翻譯成我們對(duì)應(yīng)的asm插件(gradle)的代碼
先寫(xiě)一個(gè)測(cè)試文件柳沙,里面寫(xiě)好我們的方法
然后在IDE中岩灭,借助IDE的ASM插件,查看對(duì)應(yīng)的字節(jié)碼赂鲤。如下圖所示
- 通過(guò)ASM插件查看對(duì)應(yīng)的ASM代碼:
- 接著查看MethodVisitor的api噪径,找到與字節(jié)碼對(duì)應(yīng)的數(shù)據(jù)。示例結(jié)果如下:
事件處理的sdk:
TouchEventDispatcher:事件分發(fā)的入口数初。事件接收找爱、校驗(yàn)
數(shù)據(jù)校驗(yàn)
數(shù)據(jù)包裝成自己的對(duì)象(自行定義)
通過(guò)WeakReference解除對(duì)原有的ctx的強(qiáng)引用
TouchEventCollector:采集原始的事件序列。
-
生成事件序列:
以MotionEvent.ACTION_DOWN開(kāi)始
以MotionEvent.ACTION_UP泡孩、MotionEvent.ACTION_CANCEL车摄、MotionEvent.ACTION_POINTER_UP結(jié)束
多指觸控?cái)?shù)據(jù)剔除掉。具體看業(yè)務(wù)需求
TouchEventClassification:事件序列歸類(lèi)仑鸥。
MotionEvent.ACTION_CANCEL結(jié)尾的事件序列丟棄
對(duì)MotionEvent.ACTION_MOVE事件做采樣處理
歸類(lèi)為我們需要的:?jiǎn)螕羲辈ァ㈦p擊、長(zhǎng)按事件序列
TouchEventReporter:事件上報(bào)眼俊。具體如何上報(bào)到服務(wù)器意狠,自行實(shí)現(xiàn)
如何查看日志:
開(kāi)發(fā)或者構(gòu)建過(guò)程中,在Run/Build下查看日志輸出:
項(xiàng)目運(yùn)行起來(lái)后泵琳,在logcat中查看:
搜索TouchEventDispatcher摄职、TouchEventCollector、TouchEventClassification获列、TouchEventReporter等關(guān)鍵字谷市,即可查看對(duì)應(yīng)的日志
README:更多細(xì)節(jié),請(qǐng)看README
https://github.com/tinyvampirepudge/hook-touch-event#readme
FAQ:
gradle版本從v4切換到v7后击孩,修改了gradle版本和jdk版本之后迫悠,執(zhí)行g(shù)radle命令發(fā)布倉(cāng)庫(kù)到本地,依舊報(bào)jdk version的錯(cuò)誤巩梢。
- 查看項(xiàng)目配置创泄,我們jdk版本是11。
- 執(zhí)行的gradle命令
./gradlew clean touch-event-gradle-plugin-v7:publishToLocalRepoPublicationToMavenRepository
報(bào)錯(cuò)信息
What went wrong: A problem occurred evaluating project ':aar-module'. > Failed to apply plugin 'com.android.internal.library'. > Android Gradle plugin requires Java 11 to run. You are currently using Java 1.8. Your current JDK is located in /Library/Java/JavaVirtualMachines/jdk1.8.0_261.jdk/Contents/Home/jre You can try some of the following options: - changing the IDE settings. - changing the JAVA_HOME environment variable. - changing
org.gradle.java.home
ingradle.properties
. * Try: > Run with --stacktrace option to get the stack trace. > Run with --info or --debug option to get more log output. > Run with --scan to get full insights.
從報(bào)錯(cuò)信息可以看出括蝠,在gradle命令行的環(huán)境下鞠抑,jdk版本依舊不是期望的11。我們通過(guò)./gradlew -v
來(lái)查看下:
(base) tinytongtong@tinytonongdembp hook-touch-event % ./gradlew -v
------------------------------------------------------------
Gradle 7.3.3
------------------------------------------------------------
Build time: 2021-12-22 12:37:54 UTC
Revision: 6f556c80f945dc54b50e0be633da6c62dbe8dc71
Kotlin: 1.5.31
Groovy: 3.0.9
Ant: Apache Ant(TM) version 1.10.11 compiled on July 10 2021
JVM: 1.8.0_261 (Oracle Corporation 25.261-b12)
OS: Mac OS X 10.16 x86_64
A:
此時(shí)我們有兩種解決方式忌警,一種是想辦法修改全局的jdk環(huán)境變量中的版本搁拙,另一種就比較簡(jiǎn)單,我們通過(guò)雙擊Gradle視圖中對(duì)應(yīng)Task的方式來(lái)執(zhí)行任務(wù)(而不是./gradlew命令)。
這里推薦雙擊Gradle視圖中對(duì)應(yīng)Task的方式來(lái)執(zhí)行任務(wù)的方式箕速。
Q: Could not find tools.jar. Please check that /Library/Internet Plug-Ins/JavaAppletPlugin.plugin/Contents/Home contains a valid JDK installation.
A: https://blog.csdn.net/gongsunjinqian/article/details/121228000
Q:
A:
參考:
Chapter 4. The class
File Format
JVM指令集:Chapter 6. The Java Virtual Machine Instruction Set