前言:
本篇文章是《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)分析人員蔑水、管理人員系枪、亦或者老板希望能收集一些用戶(hù)操作的行為數(shù)據(jù)戴已,比如用戶(hù)在某個(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)擊也是類(lèi)似,需要在頁(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ù)碗暗,一共有三大類(lèi):
- 在某個(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è)面名稱(chēng),可自定義
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è)面名稱(chēng),可自定義
MobclickAgent.onPageEnd("SplashScreen");
MobclickAgent.onPause(this);
}
簡(jiǎn)而言之這一類(lèi)技術(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ù)量的用戶(hù)不喜歡更新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)型,來(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)或者例行輪詢(xún)時(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ù)先收集用戶(hù)的所有行為數(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中我們用到的接口類(lèi)描述語(yǔ)言,例如跨進(jìn)程的aidl文件轉(zhuǎn)化為java文件仿畸。 |
javac | Java Compiler | java語(yǔ)言編程編譯器食棕。全稱(chēng)javacompilation朗和。javac工具讀由java語(yǔ)言編寫(xiě)的類(lèi)和接口的定義,并將它們編譯成字節(jié)代碼的class文件簿晓。javac 可以隱式編譯一些沒(méi)有在命令行中提及的源文件眶拉。 |
dex | 轉(zhuǎn)化.class文件為Davik VM能識(shí)別的.dex文件 | 把所有的字節(jié)碼文件轉(zhuǎn)成Android DEX文件(classes.dex)。它是Android平臺(tái)上可執(zhí)行文件的類(lèi)型憔儿。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ǔ)類(lèi)庫(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ù)用戶(hù)行為洞察研究院《安卓全埋點(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)成果燎窘!