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