前言:
本篇文章是《Android自動化埋點技術探索》的第一篇,主要介紹埋點的基本概念以及幾種埋點技術實現方式的原理和差異
埋點基本概念及其意義
當一款Android應用上線后,開發人員或者運營人員,也可能是市場分析人員、管理人員、亦或者老板希望能收集一些用戶操作的行為數據,比如用戶在某個頁面點擊了多少次,應用中的某個控件被點擊了多少次,在某個頁面停留了多少時間等等,這些行為數據統一收集起來之后就可以交給數據分析師來進行數據的篩選、統計、分析、決策,而且以便于后期應用的方向性調整和技術功能調整。理論上來講,這些收集統計的數據應該出自應用的PV(頁面訪問量)或UV(獨立訪客訪問數)、或者統計應用中那些具體的頁面最受歡迎、那些控件點擊率最高or最低的場景。
舉例,對于控件被點擊多少次這個應用場景,開發人員的一般做法是在控件點擊事件中加入Log代碼,然后將此次的點擊記錄下來(自增),最終發送到服務端進行數據上傳。頁面的點擊也是類似,需要在頁面生命周期的開始加入Log代碼。但如果業務邏輯復雜,頁面眾多,控件較多,那就要在許多地方插入這些數據上傳的代碼,因此實現方式和后期維護就成為了一個問題。通過在具體的功能實現的時候,去進行數據的存儲和上傳,這種技術可以簡單理解為埋點 。那么有沒有一種侵入方式低、集成起來簡單就可以快速實現數據統計的埋點技術?
目前常見的前端埋點技術,一共有三大類:
- 在某個控件操作發生時通過預先寫好的代碼來發數據的代碼埋點
- 通過可視化界面配置控件操作與事件發生關系的可視化埋點
- 收集所有數據之后,在后端篩選需要分析的對象的“無埋點”
關于這三種方案,下面做詳細的解釋說明:
可視化埋點之代碼埋點
我相信這個可視化埋點技術是最簡單也是最容易理解的一種實現方式,通過在需要統計的功能代碼處,直接使用埋點SDK的代碼來進行上傳數據的技術,例如熟悉的友盟統計:
public void onResume() {
super.onResume();
//統計頁面,僅有Activity的應用中SDK自動調用,不需要單獨寫。
//"SplashScreen"為頁面名稱,可自定義
MobclickAgent.onPageStart("SplashScreen");
//統計時長
MobclickAgent.onResume(this);
}
public void onPause() {
super.onPause();
//僅有Activity的應用中SDK自動調用,不需要單獨寫
//保證 onPageEnd 在onPause 之前調用,因為 onPause 中會保存信息。
//"SplashScreen"為頁面名稱,可自定義
MobclickAgent.onPageEnd("SplashScreen");
MobclickAgent.onPause(this);
}
簡而言之這一類技術,是通過代碼手動設置的可視化埋點功能代碼,在APP或者界面初始化的時候,初始化第三方數據分析服務商的SDK,然后在某個事件發生時就調用SDK里面相應的數據發送接口發送數據。例如,想統計APP里面某個按鈕的點擊次數,則在APP的某個按鈕被點擊時,可以在這個按鈕對應的 OnClick 函數里面調用SDK提供的數據發送接口來發送數據。
代碼埋點的優點是一方面使用者控制精準,可以非常精確地選擇什么時候發送數據;同時,使用者可以比較方便地設置自定義屬性、自定義事件,傳遞比較豐富的數據到服務端。
當然,代碼埋點的劣勢在于:首先,埋點代價比較大,每一個控件的埋點都需要添加相應的代碼,工作量會增大,而且限定了實現該功能的角色必須是技術人員才能完成;其次是功能更新的代價較大,每一次更新埋點方案,都須要改代碼,更新完之后還要通過各個應用市場進行分發,并且總有一定數量的用戶不喜歡更新APP,這樣埋點代碼也就得不到更新了;最后,就是所有前端埋點方案都會面臨的數據傳輸時效性和可靠性的問題了,這個問題就只能通過在后端收集數據來解決了。
可視化埋點之頁面配置埋點
通過頁面配置來實現埋點的技術,其實是對第一種埋點技術-可視化代碼埋點技術的一種升級,這種方式的實現過程如下:
在嵌入了埋點 SDK 的 APP 開啟可視化埋點模式,SDK 會請求然后響應服務端的要求,APP內部定期(例如每秒)做一次截圖,而 SDK 在為 App 截圖的同時,會從Window 對象開始進行遍歷頁面的子view,得到當前視圖下所有View對象的層級關系。
拿到View對象之后就可以獲取一系列數據
服務端根據截屏和可視化信息來重新進行頁面渲染,并且根據控件的類型,來識別哪些控件是可以增加可埋點的,并且將之標識出來。
當使用者在后臺的截屏畫面上點擊了某個可埋點的控件時,后臺會要求使用者做一些事件關聯方面的配置,并且將配置信息進行保存和部署。
SDK 在啟動或者例行輪詢時拿到這些配置信息,則會通過設定的接口,為每個關聯的控件添加的點擊或者編輯行為的監聽,并在回掉函數里面調用 SDK內部的接口發送相應事件的 track 信息來反饋埋點數據
至此,通過頁面來進行配置埋點的技術大概實現過程就是這樣。這種通過頁面來進行配置的可視化埋點技術很好地解決了代碼埋點的埋點代價較大以及更新代價大兩個問題。但是,通過頁面來進行配置的可視化技術能夠覆蓋的功能有限,目前并不是所有的控件操作都可以通過這種方案進行定制;同時,這種可視化埋點技術方案對自己設置屬性有一定的缺陷,例如,一個界面上有一個文本框和一個按鈕,通過可視化埋點設置點擊按鈕為一個“提交”事件時,并不能將文本框的內容作為事件的屬性進行上傳的,因此,對于可視化埋點這種方案,在上傳事件時,就只能上傳 SDK 自動收集的設備、地域、網絡等默認屬性,以及一些通過代碼設置的全局公共屬性了;最后,作為前端埋點的一種方案,可視化埋點也依然沒有解決傳輸時效性和數據可靠性的問題。
自動化埋點
自動化埋點,也叫無埋點、無碼埋點、全埋點。自動化埋點是指預先收集用戶的所有行為數據,然后再根據實際分析需求從中提取行為數據。自動化埋點技術的整體解決思路,首先就是要找到那個被點擊的View的點擊處理邏輯(也叫原處理邏輯),然后利用一定的技術,對原處理邏輯進行“攔截”,或者在原處理邏輯的前面或者后面“插入”相應的埋點代碼,從而達到自動埋點的效果。
那么如何做到自動“攔截” View 的原點擊處理邏輯?一般是參考 Android 系統 View 點擊事件處理機制來進行的。至于如何做到自動“插入”埋點代碼,基本上都是參考編譯器對 Java 代碼的處理流程來進行的,即:JavaCode --> .java --> .class --> .dex
由于無埋點技術會涉及到apk的構建流程,這里先貢獻一張APK的構建流程圖:
在理解構建流程之前,首先要了解構建過程中各個工具的意義和主要功能,下面就對功能做詳細的敘述:
名字 | 功能 | 詳細介紹 |
---|---|---|
aapt | Android資源打包工具 | 可以查看,創建, 更新ZIP格式的文檔附件(zip, jar, apk)。也可將資源文件編譯成二進制文件。 |
aidl | Android接口描述語言轉化為.java文件的工具 | 將android中我們用到的接口類描述語言,例如跨進程的aidl文件轉化為java文件。 |
javac | Java Compiler | java語言編程編譯器。全稱javacompilation。javac工具讀由java語言編寫的類和接口的定義,并將它們編譯成字節代碼的class文件。javac 可以隱式編譯一些沒有在命令行中提及的源文件。 |
dex | 轉化.class文件為Davik VM能識別的.dex文件 | 把所有的字節碼文件轉成Android DEX文件(classes.dex)。它是Android平臺上可執行文件的類型。dx工具的主要工作是將Java字節碼轉成成Dalvik字節碼、壓縮常量池、消除冗余信息等。 |
apkbuilder | 生成apk包 | 將所有沒有編譯的資源(如images等)、編譯過的資源和.dex文件都會打包到最終的.apk文件中。 |
jarsigner | jar文件的簽名工具 | 工具利用密鑰倉庫中的信息來產生或校驗 Java 存檔 (JAR) 文件的數字簽名 |
zipalign | 字節碼對齊工具 | 它能夠對打包的應用程序進行優化。 |
那么,APK構建的執行步驟分別就是:
步驟1.
使用aapt工具生成R.java文件
將Resource文件(就是工程中res中的文件)、Assets文件(相當于另外一種資源,這種資源Android系統并不像對res中的文件那樣優化它)、AndroidManifest.xml文件(包名就是從這里讀取的,因為生成R.java文件需要包名)、Android基礎類庫(Android.jar文件)打包成資源(一般在Android工程的bin目錄可以看到一個叫resources.ap_的文件就是它了)、R.java文件(在gen目錄中)。
步驟 2.
處理AIDL文件,生成對應的.java文件
將源碼文件、aidl文件、framework.aidl等應用到aidl的描述語言文件轉化為java文件。
步驟 3.
編譯Java文件,生成對應的.class文件
將源碼文件(包括R.java和AIDL生成的.java文件)、庫文件(.jar文件)編譯為.class文件。
步驟 4.
把.class文件轉化成Davik VM支持的.dex文件
將任何第三方的libraries和.class文件都會被轉換成.dex文件。dex工具生成可供Android系統Dalvik虛擬機執行的classes.dex文件。
步驟 5.
打包生成APK文件
所有沒有編譯的資源(如images等)、編譯過的資源和.dex文件都會被apkbuilder工具打包到最終的.apk文件中。
步驟 6.
對APK文件進行簽名
一旦APK文件生成,它必須被簽名才能被安裝在設備上。在開發過程中,主要用到的就是兩種簽名的keystore。一種是用于調試的debug.keystore,它主要用于調試,在Eclipse或者Android Studio中直接run以后跑在手機上的就是使用的debug.keystore。另一種就是用于發布正式版本的keystore。
步驟 7.
對簽名后的APK文件進行對齊處理
如果發布的apk是正式版的話,就必須用到的工具zipalign對APK進行對齊處理。對齊的主要過程是將APK包中所有的資源文件距離文件起始偏移為4字節整數倍,這樣通過內存映射訪問apk文件時的速度會更快。對齊的作用就是減少運行時內存的使用。
因此,無埋點技術實現的基本原理,就是利用某些技術對某些方法(View 被點擊時的處理邏輯)進行代理(或者叫 Hook),或者叫在指定的時機 插入代碼。
那么,如何進行代理,又該如何在指定的時機進行 插入代碼?
按照 “在什么時候去代理或者插入代碼”這個條件來區分的話,Android無埋點技術可以大致分為下面兩種方式 :
靜態代理
所謂靜態代理,就是指通過 Gradle Plugin 在 編譯期間 “插入”或者修改代碼(.class 文 件)。比如 AspectJ、 ASM、javassist、AST 等方案均是這種方式來實現在編譯期間“插入”或者修改代碼
動態代理
所謂動態代理,就是指在 代碼運行 的時候去進行代理。比如開發中比較常見的代理 View.OnClickListener、Window.Call-back、以及View.AccessibilityDelegate 等方案均是這種方式。
總之:不同的方案,其處理和運行效率也有很大的差異,對 App的浸入程度以及對 App 的整體性能的影響也各不相同。而且Android系統的不斷升級,不管是 Android 系統本身,還是與 Android App 開發相關的組件和技術,都在與時俱進。因此針對不同的應用場景,要具體問題具體分析。
本篇文章是《Android自動化埋點技術探索》的第一篇,主要介紹埋點的基本概念與幾種實現方式差異進行對比,接下來的文章主要對自動化埋點技術這一種實現方式進行分析和探索。
文章部分內容選自:神策數據用戶行為洞察研究院《安卓全埋點技術白皮書》,感謝技術分享!
如果這篇文章對您有開發or學習上的些許幫助,希望各位看官留下寶貴的star,謝謝。
Ps:著作權歸作者所有,轉載請注明作者, 商業轉載請聯系作者獲得授權,非商業轉載請注明出處(開頭或結尾請添加轉載出處,添加原文url地址),文章請勿濫用,也希望大家尊重筆者的勞動成果!