您好,登錄后才能下訂單哦!
作者 | 個(gè)推高級(jí)安卓開(kāi)發(fā)工程師 ?一七
一、背景
運(yùn)營(yíng)者能夠?qū)τ脩粜袨檫M(jìn)行分析的前提,是對(duì)大量數(shù)據(jù)的掌握。在以往,這個(gè)數(shù)據(jù)通常是由開(kāi)發(fā)者在控件點(diǎn)擊、頁(yè)面等事件中,一行行地編寫(xiě)埋點(diǎn)代碼來(lái)完成數(shù)據(jù)收集的。然而傳統(tǒng)的操作模式每當(dāng)升級(jí)改版時(shí),開(kāi)發(fā)和測(cè)試人員就需要重復(fù)不斷對(duì)代碼進(jìn)行更新,整個(gè)流程耗時(shí)長(zhǎng),無(wú)法滿足業(yè)務(wù)的需求。
為幫助開(kāi)發(fā)者解決這一痛點(diǎn),個(gè)推應(yīng)用統(tǒng)計(jì)“個(gè)數(shù)”推出“可視化埋點(diǎn)”這一技術(shù)來(lái)更高效地實(shí)現(xiàn)這個(gè)這一過(guò)程?!皞€(gè)數(shù)”的可視化埋點(diǎn)靈活、方便,開(kāi)發(fā)者不需對(duì)數(shù)據(jù)追蹤點(diǎn)添加任何代碼,只需要連接管理臺(tái)并圈選頁(yè)面中需要埋點(diǎn)的元素,即可添加隨時(shí)生效的界面追蹤點(diǎn)。
本文將結(jié)合個(gè)數(shù)實(shí)踐經(jīng)驗(yàn),對(duì)可視化埋點(diǎn)中的兩大關(guān)鍵技術(shù)點(diǎn)即控件唯一標(biāo)識(shí)和事件采集進(jìn)行分析并提供解決方案。
二、可視化埋點(diǎn)關(guān)鍵技術(shù)點(diǎn)
可視化埋點(diǎn)的難點(diǎn),或者說(shuō)核心就是如何在開(kāi)發(fā)者不編寫(xiě)任何代碼的情況下,SDK 如何確定任意一個(gè)控件在該應(yīng)用內(nèi)的唯一性,以及如何監(jiān)聽(tīng)控件的點(diǎn)擊和頁(yè)面的切換。
標(biāo)識(shí)
為了防止不同頁(yè)面中的控件標(biāo)識(shí)重復(fù)出現(xiàn),控件的唯一標(biāo)識(shí)一般由頁(yè)面標(biāo)識(shí)加上控件標(biāo)識(shí)生成。
頁(yè)面標(biāo)識(shí)生成
頁(yè)面標(biāo)識(shí)可以直接使用頁(yè)面的名稱,即 Activity name。其獲取方式比較多,這里介紹一種比較通用的方法,即通過(guò)注冊(cè) Application.ActivityLifecycleCallbacks ,開(kāi)發(fā)者可以在以下生命周期的回調(diào)中,輕松地拿到當(dāng)前的 Activity 對(duì)象。此方法適用于一個(gè) Activity 并無(wú) Fragment 存在的情形。
代碼詳見(jiàn)下圖:
獲取方式也是比較多,不過(guò)較于 Activity 的獲取會(huì)相對(duì)麻煩一些,因?yàn)橄到y(tǒng)沒(méi)有直接提供 API ,因而需要稍微轉(zhuǎn)個(gè)彎:通過(guò) Gradle 插樁的方式,獲取 Fragment 的生命周期,以及 Fragment 實(shí)例對(duì)象:
如果該應(yīng)用的頁(yè)面存在一個(gè) Activity 中嵌套多個(gè) Fragment 的情況,單單一個(gè) Activity name則可能無(wú)法精準(zhǔn)地定位到某個(gè)頁(yè)面,因而還需要加上 Fragment 的名稱。Fragment的獲取可以通過(guò) Gradle 插樁法來(lái)實(shí)現(xiàn),即根據(jù) Fragment 的生命周期來(lái)獲取Fragment 實(shí)例對(duì)象。
1.2控件標(biāo)識(shí)生成
理想的情況下頁(yè)面中的每個(gè)控件都有屬于自己的唯一 id,SDK 直接獲取控件的 id 當(dāng)做控件標(biāo)識(shí)即可。但現(xiàn)實(shí)情況卻是,一個(gè)頁(yè)面中往往存在多個(gè)相同 id 的控件,或者是沒(méi)有 id 的控件,比如 Listview 的 item ,開(kāi)發(fā)者不可能給listview的每個(gè)item 設(shè)置不同的 id。
因此需要轉(zhuǎn)變一下思路。我們可以從控件路徑這個(gè)除id 外比較獨(dú)特的性質(zhì)著手來(lái)生成控件標(biāo)識(shí)。開(kāi)發(fā)者可以通過(guò)給控件的路徑加上控件角標(biāo)的結(jié)構(gòu)方式,生成控件的唯一標(biāo)識(shí)。下圖是Github 上一個(gè)仿 B 站的應(yīng)用。我們對(duì)這個(gè)應(yīng)用進(jìn)行一下控件樹(shù)分析。首先我們使用 Android Studio 自帶的 UI Automator Viewer 工具查看該頁(yè)面的布局結(jié)構(gòu):
接下來(lái),我們可以從Application.ActivityLifecycleCallbacks 的回調(diào)中拿到 Activity 實(shí)例,再使用 activity.getWindow().getDecorView().getRootView() 方法來(lái)獲取當(dāng)前頁(yè)面的控件樹(shù)。
例如圖中的文字控件是 TextView,且無(wú)兄弟布局,則可以標(biāo)記為 TextView[0] 。它的父布局是 LinearLayout 且排在兄弟布局中的第二位,那么就可以寫(xiě)成是 LinearLayout[1],然后使用自己定義的符號(hào)拼接,像是 LinearLayout[1]/TextView[0] 。之后以此類推、循環(huán)遍歷、層層遞進(jìn),將所有經(jīng)過(guò)的控件以及它們的下標(biāo)都拼接起來(lái),組成控件在該頁(yè)面中的唯一標(biāo)識(shí)。
對(duì)于一些可復(fù)用的 View ,我們則需要采取一些特殊處理。例如對(duì)于 RecyclerView、ListView、 ViewPager 等復(fù)用控件,我們都需要采取不同的處理方式,去獲取當(dāng)前 View 在該控件中的具體下標(biāo)。如果沒(méi)有進(jìn)行特殊處理,則會(huì)導(dǎo)致子控件錯(cuò)位,數(shù)據(jù)統(tǒng)計(jì)不準(zhǔn)確。
采集
在以往的處理中,如果需要知道一個(gè)按鈕的點(diǎn)擊次數(shù),開(kāi)發(fā)者就要在該控件的click事件中加入對(duì)應(yīng)的打點(diǎn)代碼。這種重復(fù)勞作,無(wú)疑增加了開(kāi)發(fā)者的開(kāi)發(fā)負(fù)擔(dān)。對(duì)此,我們可以采用動(dòng)態(tài)代理方式或Gradle 插樁方式來(lái)改善這個(gè)問(wèn)題。
動(dòng)態(tài)代理方式
使用安卓自帶的輔助功能 View.AccessibilityDelegate 。前文提到當(dāng)頁(yè)面變化時(shí),我們可以通過(guò) Application.ActivityLifecycleCallbacks 獲取到 Activity 的實(shí)例對(duì)象,接著根據(jù)activity.getWindow().getDecorView().getRootView() 來(lái)獲取到控件樹(shù)。由于控件樹(shù)可能會(huì)實(shí)時(shí)發(fā)生變化,我們則需要通過(guò) ViewTreeObserver.OnGlobalLayoutListener 的方法監(jiān)聽(tīng)視圖變化,從而在該回調(diào)中拿到變化的控件。接著我們 要根據(jù)遞歸判斷該控件是否為 ViewGroup、是否可以點(diǎn)擊、是否能夠顯示等,繼而給符合條件的 View 設(shè)置 sendAccessibilityEvent();此外,我們還要在繼承了 View.AccessibilityDelegate 的定義類中,對(duì)以下這些方法添加 SDK 的代理:
當(dāng)對(duì)應(yīng)的控件被點(diǎn)擊時(shí),系統(tǒng)就會(huì)自動(dòng)調(diào)用設(shè)置過(guò)代理的方法,存儲(chǔ)或者上報(bào)對(duì)應(yīng)數(shù)據(jù)。
Gradle 插樁的方式
Android Gradle 工具在1.5.0 版本后提供了 Transfrom API , 該API 允許第三方 Plugin 在打包 dex 文件之前的編譯過(guò)程中操作 .class 文件。在編譯期,開(kāi)發(fā)者可以通過(guò)onClick、onItemClick等方法(詳見(jiàn)下圖)進(jìn)行監(jiān)聽(tīng),這相當(dāng)于是正則匹配。
當(dāng)上述監(jiān)聽(tīng)的方法被編譯的時(shí)候,就可以將埋點(diǎn)的代理操作插入這些方法中,實(shí)現(xiàn)自動(dòng)化埋點(diǎn)的流程。網(wǎng)上相關(guān)流程也是非常詳細(xì),有興趣的可以自行搜索學(xué)習(xí)。
三、結(jié)語(yǔ)
以上就是APP端可視化埋點(diǎn)實(shí)現(xiàn)過(guò)程中的關(guān)鍵點(diǎn),特別需要注意的是控件唯一標(biāo)識(shí)那一塊,由于布局千變?nèi)f化,開(kāi)發(fā)者針對(duì)很多特定的布局都需要采取對(duì)應(yīng)的處理方式。
目前個(gè)推應(yīng)用統(tǒng)計(jì)——個(gè)數(shù)這個(gè)產(chǎn)品只需要一行初始化代碼就可以自動(dòng)幫助開(kāi)發(fā)者采集包括頁(yè)面統(tǒng)計(jì)、事件埋點(diǎn)、新增活躍等多維度信息。
借著萬(wàn)圣節(jié),悄悄跟你說(shuō)個(gè)恐怖的故事:除個(gè)推應(yīng)用統(tǒng)計(jì)服務(wù)之外,VIP消息推送、用戶畫(huà)像,現(xiàn)在申請(qǐng)均可以免費(fèi)用一年!一鍵認(rèn)證服務(wù)還可以享受充10萬(wàn)條送5萬(wàn)條的優(yōu)惠哦 !點(diǎn)擊https://www.getui.com/2019devfest,“碼“上申請(qǐng)吧!
行業(yè)前沿、面試寶典,更多技術(shù)干貨,盡在個(gè)推技術(shù)學(xué)院。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。