日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

揭秘!一个高准确率的Flutter埋点框架如何设计

發(fā)布時(shí)間:2024/8/23 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 揭秘!一个高准确率的Flutter埋点框架如何设计 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

背景

用戶行為埋點(diǎn)是用來記錄用戶在操作時(shí)的一系列行為,也是業(yè)務(wù)做判斷的核心數(shù)據(jù)依據(jù),如果缺失或者不準(zhǔn)確將會(huì)給業(yè)務(wù)帶來不可恢復(fù)的損失。閑魚將業(yè)務(wù)代碼從Native遷移到Flutter上過程中,發(fā)現(xiàn)原先Native體系上的埋點(diǎn)方案無法應(yīng)用在Flutter體系之上。而如果我們只把業(yè)務(wù)功能遷移過來就上線,對業(yè)務(wù)是極其不負(fù)責(zé)任的。因此,經(jīng)過不斷探索,我們沉淀了一套Flutter上的高準(zhǔn)確率的用戶行為埋點(diǎn)方案。

用戶行為埋點(diǎn)定義

先來講講在我們這里是如何定義用戶行為埋點(diǎn)的。在如下用戶時(shí)間軸上,用戶進(jìn)入A頁面后,看到了按鈕X,然后點(diǎn)擊了這個(gè)按鈕,隨即打開了新的頁面B。

這個(gè)時(shí)間軸上有如下5個(gè)埋點(diǎn)事件發(fā)生:

  • 進(jìn)入A頁面。A頁面首幀渲染完畢,并獲得了焦點(diǎn)。
  • 曝光坑位X。按鈕X處于手機(jī)屏幕內(nèi),且停留一段時(shí)間,讓用戶可見可觸摸。
  • 點(diǎn)擊坑位X。用戶對按鈕X的內(nèi)容很感興趣,于是點(diǎn)擊了它。按鈕X響應(yīng)點(diǎn)擊,然后需要打開一個(gè)新頁面。
  • 離開A頁面。A頁面失去焦點(diǎn)。
  • 進(jìn)入B頁面。B頁面首幀渲染完畢,并獲得焦點(diǎn)。

在這里,打埋點(diǎn)最重要的是時(shí)機(jī),即在什么時(shí)機(jī)下的事件中觸發(fā)什么埋點(diǎn),下面來看看閑魚在Flutter上的實(shí)現(xiàn)方案。

實(shí)現(xiàn)方案

進(jìn)入/離開頁面

在Native原生開發(fā)中,Android端是監(jiān)聽Activity的onResume和onPause事件來做為頁面的進(jìn)入和離開事件,同理iOS端是監(jiān)聽UIViewController的viewWillAppear和viewDidDisappear事件來做為頁面的進(jìn)入和離開事件。同時(shí)整個(gè)頁面棧是由Android和iOS操作系統(tǒng)來維護(hù)。

在Flutter中,Android和iOS端分別是用FlutterActivity和FlutterViewController來做為容器承載Flutter的頁面,通過這個(gè)容器可以在一個(gè)Native的頁面內(nèi)(FlutterActivity/FlutterViewController)來進(jìn)行Flutter原生頁面的切換。即在Flutter自己維護(hù)了一個(gè)Flutter頁面的頁面棧。這樣,原來我們最熟悉的那套在Native原生上方案在Flutter上無法直接運(yùn)作起來。

針對這個(gè)問題,可能很多人會(huì)想到去注冊監(jiān)聽Flutter的NavigatorObserver,這樣就知道Flutter頁面的進(jìn)棧(push)和出棧(pop)事件。但是這會(huì)有兩個(gè)問題:

  • 假設(shè)A、B兩個(gè)頁面先后進(jìn)棧(A enter -> A leave -> B enter)。然后B頁面返回退出(B leave),此時(shí)A頁面重新可見,但是此時(shí)是收不到A頁面push(A enter)的事件。
  • 假設(shè)在A頁面彈出一個(gè)Dialog或者BottomSheet,而這兩類也會(huì)走push操作,但實(shí)際上A頁面并未離開。

好在Flutter的頁面棧不像Android Native的頁面棧那么復(fù)雜,所以針對第一個(gè)問題,我們可以來維護(hù)一個(gè)和頁面棧匹配的索引列表。當(dāng)收到A頁面的push事件時(shí),往隊(duì)列里塞一個(gè)A的索引。當(dāng)收到B頁面的push事件時(shí),檢測列表內(nèi)是否有頁面,如有,則對列表最后一個(gè)頁面執(zhí)行離開頁面事件記錄,然后再對B頁面執(zhí)行進(jìn)入頁面事件記錄,接著往隊(duì)列里塞一個(gè)B的索引。當(dāng)收到B頁面的pop事件時(shí),先對B頁面執(zhí)行離開頁面事件記錄,然后對隊(duì)列里存在的最后一個(gè)索引對應(yīng)的頁面(假設(shè)為A)進(jìn)行判斷是否在棧頂(ModalRoute.of(context).isCurrent
),如果是,則對A頁面執(zhí)行進(jìn)入頁面事件記錄。

針對第二個(gè)問題,Route類內(nèi)有個(gè)成員變量overlayEntries,可以獲取當(dāng)前Route對應(yīng)的所有圖層OverlayEntry,在OverlayEntry對象中有個(gè)成員變量opaque可以判斷當(dāng)前這個(gè)圖層是否全屏覆蓋,從而可以排除Dialog和BottomSheet這種類型。再結(jié)合問題1,還需要在上述方案中加上對push進(jìn)來的新頁面來做判斷是否為一個(gè)有效頁面。如果是有效頁面,才對索引列表中前一個(gè)頁面做離開頁面事件,且將有效頁面加到索引列表中。如果不是有效頁面,則不操作索引列表。

以上并不是閑魚的方案,只是筆者給出的一個(gè)建議。因?yàn)殚e魚APP在一開始落地Flutter框架時(shí),就沒有使用Flutter原生的頁面棧管理方案,而是采用了Native+Flutter混合開發(fā)的方案。具體可參考前面的一篇文章《已開源|碼上用它開始Flutter混合開發(fā)——FlutterBoost》。因此接下來也是基于此來闡述閑魚的方案。

閑魚的方案如下(以Android為例,iOS同理):

注:首次打開指的是基于混合棧新打開一個(gè)頁面,非首次打開指的是通過回退頁面的方式,在后臺(tái)的頁面再次到前臺(tái)可見。

看似我們將何時(shí)去觸發(fā)進(jìn)入/離開頁面事件的判斷交給Flutter側(cè),實(shí)際上依然跟Native側(cè)的頁面棧管理保持了一致,將原先在Native側(cè)做打埋點(diǎn)的時(shí)機(jī)告知Flutter側(cè),然后Flutter側(cè)再立刻通過channel來調(diào)用Native側(cè)的打埋點(diǎn)方法。那么可能會(huì)有人問,為什么這么繞,不全部交給Native側(cè)去直接管理呢?交給Native側(cè)去直接管理這樣做針對非首次打開這個(gè)場景是合適的,但是對首次打開這個(gè)場景卻是不合適的。因?yàn)樵谑状未蜷_這個(gè)場景下,onResume時(shí)Flutter頁面尚未初始化,此時(shí)還不知道頁面信息,因此也就不知道進(jìn)入了什么頁面,所以需要在Flutter頁面初始化(init)時(shí)再回過來調(diào)Native側(cè)的進(jìn)入頁面埋點(diǎn)接口。為了避免開發(fā)人員去關(guān)注是否為首次打開Flutter頁面,因此我們統(tǒng)一在Flutter側(cè)來直接觸發(fā)進(jìn)入/離開頁面事件。

曝光坑位

先講下曝光坑位在我們這里的定義,我們認(rèn)為圖片和文本是有曝光意義的,其他用戶看不見的是沒有曝光意義的,在此之上,當(dāng)一個(gè)坑位同時(shí)滿足以下兩點(diǎn)時(shí)才會(huì)被認(rèn)為是一次有效曝光:

  • 坑位在屏幕可見區(qū)域中的面積大于等于坑位整體面積的一半。
  • 坑位在屏幕可見區(qū)域中停留超過500ms。

基于此定義,我們可以很快得出如下圖所示的場景,在一個(gè)可以滾動(dòng)的頁面上有A、B、C、D共4個(gè)坑位。其中:

  • 坑位A已經(jīng)滑出了屏幕可見區(qū)域,即invisible;
  • 坑位B即將向上從屏幕中可見區(qū)域滑出,即visible->invisible;
  • 坑位C還在屏幕中央可視區(qū)域內(nèi),即visible;
  • 坑位D即將滑入屏幕中可見區(qū)域,invisible->visible;

那么我們的問題就是如何算出坑位在屏幕內(nèi)曝光面積的比例。要算出這個(gè)值,需要知道以下幾個(gè)數(shù)值:

  • 容器相對屏幕的偏移量
  • 坑位相對容器的偏移量
  • 坑位的位置和寬高
  • 容器的位置和寬高

其中坑位和容器的寬和高很容易獲取和計(jì)算,這里就不再累述。

獲取容器相對屏幕的偏移量

//監(jiān)聽容器滾動(dòng),得到容器的偏移量 double _scrollContainerOffset = scrollNotification.metrics.pixels;

獲取坑位相對容器的偏移量

//曝光坑位Widget的context final RenderObject childRenderObject = context.findRenderObject(); final RenderAbstractViewport viewport = RenderAbstractViewport.of(childRenderObject); if (viewport == null) {return; } if (!childRenderObject.attached) {return; } //曝光坑位在容器內(nèi)的偏移量 final RevealedOffset offsetToRevealTop = viewport.getOffsetToReveal(childRenderObject, 0.0);

邏輯判斷

if (當(dāng)前坑位是invisible && 曝光比例 >= 0.5) {記錄當(dāng)前坑位是visible狀態(tài)記錄出現(xiàn)時(shí)間 } else if (當(dāng)前坑位是visible && 曝光比例 < 0.5) {記錄當(dāng)前坑位是invisible狀態(tài)if (當(dāng)前時(shí)間-出現(xiàn)時(shí)間 > 500ms) {調(diào)用曝光埋點(diǎn)接口} }

點(diǎn)擊坑位

點(diǎn)擊坑位埋點(diǎn)沒什么難點(diǎn),很容易就可以想到下面的方案:

效果

經(jīng)過多輪迭代和優(yōu)化,目前線上Flutter頁面的埋點(diǎn)準(zhǔn)確率已經(jīng)達(dá)到100%,有力地支持了業(yè)務(wù)的分析和判斷。同時(shí)這套方案讓業(yè)務(wù)同學(xué)在做開發(fā)時(shí),對于頁面進(jìn)入/離開、曝光坑位可以做到無感知,即不用關(guān)心何時(shí)去觸發(fā),做到了簡單易用和無侵入性。

展望

此外,針對頁面進(jìn)入/離開這個(gè)場景,由于閑魚是基于Flutter Boost混合棧的方案,因此我們的解決方案還不夠通用。不過未來隨著閑魚上的Flutter頁面越來越多,我們后續(xù)也會(huì)去實(shí)現(xiàn)基于Flutter原生的方案。

原文鏈接
本文為云棲社區(qū)原創(chuàng)內(nèi)容,未經(jīng)允許不得轉(zhuǎn)載。

總結(jié)

以上是生活随笔為你收集整理的揭秘!一个高准确率的Flutter埋点框架如何设计的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。