互聯網產品對用戶行為的數據有多渴望,相信我也不用多說了,上至微信,下至小作坊,每一個公司每一個有稍微專業點的產品經理或者技術人員,都想知道做出來的產品用戶到底有沒有在用那些功能用得多,用的頻率怎樣。淘寶、天貓、京東這些APP為什么每次打開的時候看到的東西感覺都挺熟悉?是不是上幾次查詢的商品?這些都是典型的用戶行為分析得出來的定制結果。
人都有惰性,都很懶。技術人員更懶,都想按一個回車就不想管事情。那用戶行為的數據怎么拿到?每一個方法前后都要hard code一句話?那開發不得累死。有沒有更好更聰明的辦法?畢竟是人都想偷懶,都想上班摸魚。當然有,下面說得內容會涉及幾個關鍵詞:AOP、AspectJ、Gradle、plugin、ASM
基于用戶行為數據的重要性,我在公司內部推動了一項全埋點數據收集的專項項目。主要的解決方案來自《Android全埋點解決方案》這本書,現學現用。
一、Activity生命周期狀態監控
通過Application的registerActivityLifecycleCallbacks方法,監聽所有Activity的生命周期回調。書中的例子只監聽onActivityResumed回調,因為onResumed方法必定會執行。
源碼:https://github.com/wangzhzh/AutoTrackAppViewScreen
二、AppStart、AppEnd事件監控
記錄AppStart事件:依然是通過Application的registerActivityLifecycleCallbacks方法,監聽onActivityStarted回調,記錄AppStart事件。
記錄AppEnd事件:內部實現一個CountDownTimer計數器,如果Activity執行了onActivityPaused回調,則計數器開始工作,倒計時30秒后則是做AppEnd。
這里有一個問題需要處理,由于Android是一個多進程的實現邏輯,如果一個程序有多進程的實現邏輯,如果另一個進程依然在工作,則不能執行AppEnd的事件統計。一個進程怎么知道另一個進程是否在執行任務?可以用ContentProvider+SQLite+ContentObserver。
源碼:https://github.com/wangzhzh/AutoTrackAppStartAppEnd
三、AppClick事件監控
控件的點擊事件記錄是最麻煩的,實現方案有好幾種。
1、代理ClickListener方式
源碼:https://github.com/wangzhzh/AutoTrackAppClick1
2、代理Window.Callback
源碼:https://github.com/wangzhzh/AutoTrackAppClick2
3、4、代理View.AccessibilityDelegate、 透明層
源碼:https://github.com/wangzhzh/AutoTrackAppClick3
源碼:https://github.com/wangzhzh/AutoTrackAppClick4
以上方式或多或少都有各自的問題,要么用了反射,影響性能,要么用了新api的方法,對舊系統不兼容。強伯癥的人會糾結的要死。
5、AspectJ
AOP,即面向切面編程。在Spring框架上已經存在很長時間了。
AspectJ,是一個基于AOP思想來實現的一個框架,它能提供ajc編譯器,將配置好的內容在編譯階段寫進源代碼中,以實現“織入”的操作。
要使用AspectJ框架有2種方式
1、直接在Gradle腳本編寫配置信息
源碼:https://github.com/wangzhzh/AutoTrackAspectJProject1
2、通過Gradle Plugin插件的形式,配置AspectJ框架
源碼:https://github.com/wangzhzh/AutoTrackAspectJProject2
這里介紹了如何使用Gradle Extentions來擴展Gradle配置腳本。
用AspectJ框架實現埋點監控
源碼:https://github.com/wangzhzh/AutoTrackAppClick5
PS:call與execution的區別
call
ViewOnClickListenerAspectj.aspectOf().aopTestMethod();
this.myTestMethod();
execution
pulic void myTestMethod() {
ViewOnClickListenerAspectj.aspectOf().aopTestMethod();
// TO-DO
}
AspectJ框架有缺點:
1、無法織入第三方庫
2、無法兼容Lambda語法
3、有兼容性問題,D8、Gradle4.X
6、ASM
Android應用程序的打包流程有需要的自行查閱。Goodle提供了Transform API,用于.class文件打包成.dex文件之前實現想要的操作。ASM是Java字節碼操作和分析框架,可以通過ASM實現動態改變類或增強既有類的功能。如,在click方法后增加埋點記錄邏輯。
Gradle Transform操作例子:
源碼:https://github.com/wangzhzh/AutoTrackTransformProject
這里的實現是每次都是清除舊的構建記錄,再新生成編譯后的代碼。
ASM核心類:ClassReader、ClassVisitor、AdviceAdapter
ClassVisitor會遍歷類中的所有成員。
用ASM框架實現埋點監控
源碼:https://github.com/wangzhzh/AutoTrackAppClick6
核心代碼:
Method visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
···
onMethodExit(int opcode) {
if ((mInterfaces != null && mInterfaces.length > 0) || isSensorsDataTrackViewOnClickAnnotation) {
if ((mInterfaces.contains('android/view/View$OnClickListener') && nameDesc == 'onClick(Landroid/view/View;)V') ||
desc == '(Landroid/view/View;)V') {
methodVisitor.visitVarInsn(ALOAD, 1)
methodVisitor.visitMethodInsn(INVOKESTATIC, SDK_API_CLASS, "trackViewOnClick", "(Landroid/view/View;)V", false)
}
}
···
}
ALOAD、ILOAD區別
ALOAD:指加載對象類型的參數,如 UserEntity
ILOAD:指加載基礎類型的參數,如 int、long、boolean等
7、8 Javassist、AST
這兩種方式不太好理解,自行查閱相關代碼。
源碼:https://github.com/wangzhzh/AutoTrackAppClick7
源碼:https://github.com/wangzhzh/AutoTrackAppClick8
綜上所述,最完美的解決方案是ASM,不用反射,沒有兼容性問題,語法又清晰。最終確定用ASM方式進行埋點數據記錄。另外涉及到數據保存、清理、上報的邏輯,用Google的room進行數據保存,定期清理已上報的數據,上報網絡組件是基本的Http請求。公司項目暫不開源,謝謝!