91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何進行Android埋點技術分析

發布時間:2021-11-26 16:06:35 來源:億速云 閱讀:223 作者:柒染 欄目:移動開發

本篇文章為大家展示了如何進行Android埋點技術分析,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

一、埋點,是對網站、App或者后臺等應用程序進行數據采集的一種方法。通過埋點,可以收集用戶在應用中的產生行為,進而用于分析和優化產品后續的體驗,也可以為產品的運營提供數據支撐,其中常見的指標有PV、UV、頁面時長和按鈕的點擊等。

采集行為數據時,通常需要在Web頁面/App里面添加一些代碼,當用戶的行為達到某種條件時,就會向服務器上報用戶的行為。其實添加這些代碼的過程就可以叫做“埋點”,在很久以前就已經出現了這種技術。隨著技術的發展和大家對數據采集要求的不斷提高,我認為埋點的技術方案走過了下面幾個階段:

代碼埋點:代碼埋點是指開發人員按照產品/運營的需求,在Web頁面/App的源碼里面添加行為上報的代碼,當用戶的行為滿足某一個條件時,這些代碼就會被執行,向服務器上報行為數據。這種方案是最基礎的方案,每次增加或者修改數據上報的條件,都需要開發人員的參與,并且只能在下一個版本上線后才能看到效果。很多公司都提供了這類數據上報的SDK,將行為上報的后臺服務器接口封裝成了簡單的客戶端SDK接口。開發者可以通過嵌入這類SDK,在埋點的地方調用少量的代碼就可以上報行為數據。

全埋點:全埋點指的是將Web頁面/App內產生的所有的、滿足某個條件的行為,全部上報到后臺服務器。例如把App中所有的按鈕點擊都進行上報,然后由產品/運營去后臺篩選所需要的行為數據。這種方案的優點非常明顯,就是可以不用在新增/修改行為上報條件時,再找開發人員去修改埋點的代碼。然而它的缺點也和優點一樣明顯,那就是上報的數據量比代碼埋點大很多,里面可能很多是沒有價值的數據。此外,這種方案更傾向于獨立去看待用戶的行為,而沒有關注行為的上下文,給數據分析帶來了一些難度。很多公司也提供了這類功能的SDK,通過靜態或者動態的方式,“Hook”了原有的App代碼,從而實現了行為的監測,在數據上報時通常是采用累積多條再上報的方案來合并請求。

hook直譯是鉤子的意思,以前學信息安全的時候在windows上聽到過,大體意思是通過某種手段去改變系統API的一個行為,繞過系統的某個方法,或者改變系統的工作流程。在這里其實是指把本來要執行某個方法的對象替換成另一個,一般用的是反射或者代理,需要找到hook的代碼位置,甚至還可以在編譯階段實現替換。

可視化埋點:  可視化埋點是指產品/運營在Web頁面/App的界面上進行圈選,配置需要監測界面上哪一個元素,然后保存這個配置,當App啟動時會從后臺服務器獲得產品/運營預先圈選好的配置,然后根據這份配置監測App界面上的元素,當某一個元素滿足條件時,就會上報行為數據到后臺服務器。有了全埋點技術方案,從體驗優化的角度很容易想到按需埋點,可視化埋點就是一種按需配置埋點的方案。現在也有一些公司提供了這類SDK,圈選監測元素時,一般都是提供一個Web管理界面,手機在安裝并初始化了SDK之后,可以和管理界面了連接,讓用戶在Web管理界面上配置需要監測的元素。

業界有多家SDK都支持上面介紹的3種埋點方案中的一種或者全部,例如Mixpanel、Sensorsdata、TalkingData、GrowingIO、諸葛IO、Heap  Analytics、MTA、Umeng Analytics、百度,只是大家對后兩種埋點的稱呼不完全相同,有的叫無埋點或者codeless埋點。由于  Mixpanel (支持代碼埋點、可視化埋點)和 Sensorsdata (全部支持)都開源了自己的全部SDK,技術方案也比較類似,下面以它們的Android  SDK為例,簡單分析一下3種埋點方案的技術實現。關于JS的SDK技術實現,可以看下我的另一篇博客-JS埋點SDK技術分析。

二、代碼埋點

包含Mixpanel SDK在內的大部分SDK,都會把這種埋點方案封裝成一個比較簡單的接口,在這里是 track(String eventName,  JSONObject properties) ,開發者在調用這個接口時,可以把一個事件名稱和事件的屬性傳入,然后就可以上報到后臺了。

在實現上,Mixpanel SDK默認采用一條HandlerThread線程來處理事件,當開發者調用 track(String eventName,  JSONObject properties) 方法時, 主線程切換到HandlerThread  當中,并先將事件存入數據庫。然后看SDK中是否累計到了40個事件,如果累計到40個事件的話,就合并它們上報到后臺。

當開發者設置為debug模式,或者手動調用 flush  接口時,可以立即上報累計的所有事件,不過由于只有一條線程,所以如果在flush的時候,前面的事件還沒有處理完成,SDK會間隔1分鐘再次去處理后面的這些事件。

開發者可以設置累計上報的事件數量閾值、事件阻塞時再次嘗試上報的時間間隔等。這種方案比較基礎,相信大部分開發者都接觸過,不需要過多分析。

三、全埋點

3.1 AOP基礎

Mixpanel現在的Android SDK沒有提供這個功能,但是神策Android SDK提供了,實現方式是依賴AOP。那么什么是AOP?

在軟件業,AOP為Aspect Oriented  Programming的縮寫,意為:面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術。AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生范型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。(from  baidu baike)

簡而言之,AOP是可以通過預編譯方式和運行期動態代理實現在不修改源代碼的情況下給程序動態統一添加功能的一種技術。

Sensors Analytics  AndroidSDK全埋點的實現就是通過在代碼編譯階段,找到源代碼中需要上報事件的位置,插入SDK的事件上報代碼。它用到的框架是 AspectJ 。

說到這里,必須簡單了解一下AspectJ以及它里面的一些概念.它是AOP的領跑者,在很多地方我們可以看到它的身影,例如JakeWartson大神貢獻的一個注解日志和性能調優框架  Hugo ,在Spring框架里面也大量應用到AspectJ。我理解AspectJ里面的主要幾個概念有:

  • JPoint: 代碼切點(就是我們要插入代碼的地方)

  • Aspect: 代碼切點的描述

  • Pointcut: 描述切點具體是什么樣的點,如函數被調用的地方( Call(MethodSignature) )、函數執行的內部(  execution(MethodSignature) )

  • Advice: 描述在切點的什么位置插入代碼,如在Pointcut前面( @Before )還是后面( @After ),還是環繞整個Pointcut(  @Around )

由此可見,在實現AOP功能時,需要做下面幾件事:

  • 定義一個Aspect,這個Aspect里面必須有Pointcut和Advice兩個屬性

  • 編寫在匹配到符合Pointcut和Advice描述的代碼時,需要注入的代碼

  • 在代碼編譯時,通過特殊的java編譯器(Aspect的ajc編譯器),找到符合我們定義的Aspect的代碼,將需要注入的代碼插入到Advice指定的位置。

如果你對AspectJ有了解的話,已經可以猜到SDK內部是怎么實現全埋點的了;如果沒有接觸,我覺得也不用急于全面地去學習AspectJ,因為SDK內部只用到了AspectJ當中的一小部分功能而已,可以直接看下面的分析。

3.2 全埋點-技術實現

神策SDK里面是如何監測View點擊事件呢?我把SDK代碼簡化一下進行分析,有下面幾個步驟:

3.2.1 定義一個Aspect

import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;  @Aspect public class ViewOnClickListenerAspectj{      /** * android.view.View.OnClickListener.onClick(android.view.View) * *@paramjoinPoint JoinPoint *@throwsThrowable Exception */     @After("execution(* android.view.View.OnClickListener.onClick(android.view.View))")     public void onViewClickAOP(final JoinPoint joinPoint)throws Throwable {         AopUtil.sendTrackEventToSDK(joinPoint, "onViewOnClick");     } }

這段Aspect的代碼定義了:  在執行android.view.View.OnClickListener.onClick(android.view.View)方法原有的實現后面,需要插入  AopUtil.sendTrackEventToSDK(joinPoint, "onViewOnClick"); 這段代碼。

AopUtil.sendTrackEventToSDK(joinPoint, "onViewOnClick");  這段代碼做的事情就是點擊事件的上報。因為神策SDK將全埋點功能和主SDK包分離成了兩個jar包,所以通過AopUtil工具去調用真正的事件上報代碼,這里不細述其實現,下面直接看這段代碼背后真正的點擊上報實現。

SensorsDataAPI.sharedInstance().track(AopConstants.APP_CLICK_EVENT_NAME, properties);

可以看到AOP實現的點擊監測,***也走 track 方法進行上報了。

3.2.2 使用ajc編譯器向源代碼中插入Aspect代碼

采用AspectJ框架編寫的代碼,想要注入原來的工程的代碼,需要在 /app/build.gradle 中引用ajc編譯器,腳本如下:

... import org.aspectj.bridge.IMessage import org.aspectj.bridge.MessageHandler import org.aspectj.tools.ajc.Main  buildscript {     repositories {         mavenCentral()     }     dependencies {         classpath 'org.aspectj:aspectjtools:1.8.10'     } }  repositories {     mavenCentral() }  android {     ... }  dependencies {     ...     compile 'org.aspectj:aspectjrt:1.8.10' }  final def log = project.logger final def variants = project.android.applicationVariants  variants.all { variant ->     if (!variant.buildType.isDebuggable()) {         log.debug("Skipping non-debuggable build type '${variant.buildType.name}'.")         return;     }      JavaCompile javaCompile = variant.javaCompile     javaCompile.doLast {         String[] args = ["-showWeaveInfo",                      "-1.5",                      "-inpath", javaCompile.destinationDir.toString(),                      "-aspectpath", javaCompile.classpath.asPath,                      "-d", javaCompile.destinationDir.toString(),                      "-classpath", javaCompile.classpath.asPath,                      "-bootclasspath", project.android.bootClasspath.join(File.pathSeparator)]         log.debug "ajc args: " + Arrays.toString(args)          MessageHandler handler = new MessageHandler(true);         new Main().run(args, handler);         for (IMessage message : handler.getMessages(null, true)) {            switch (message.getKind()) {                 case IMessage.ABORT:                 case IMessage.ERROR:                 case IMessage.FAIL:                     log.error message.message, message.thrown                     break;                 case IMessage.WARNING:                     log.warn message.message, message.thrown                     break;                 case IMessage.INFO:                     log.info message.message, message.thrown                     break;                 case IMessage.DEBUG:                     log.debug message.message, message.thrown                     break;             }         }     } }

在SensorsAndroidSDK中,把上面這段腳本編寫成了一個 gradle插件 ,開發者只需要在 app/build.gradle  引用這個插件即可。

apply plugin: 'com.sensorsdata.analytics.android'

3.2.3 完成代碼插入,查看插入之后的效果

完成上面兩步,就可以實現在 android.view.View.OnClickListener.onClick(android.view.View)  方法中插入我們的數據上報代碼了。我們在demo代碼中加一個Button,并給它set一個OnClickListener,編譯一下代碼,查看  /build/intermediates/classes/debug/ 里面class文件,經過ajc編譯之后,原始代碼中插入了Aspect的代碼,并調用了  ViewOnClickListenerAspectj 里面的 onViewClickAOP 方法。

public class MainActivityextends Activity{     public MainActivity(){     }      protected void onCreate(Bundle savedInstanceState){         super.onCreate(savedInstanceState);         this.setContentView(2130968603);         Button btnTst = (Button)this.findViewById(2131427422);         btnTst.setOnClickListener(new OnClickListener() {             public void onClick(View v){                 JoinPoint var2 = Factory.makeJP(ajc$tjp_0, this, this, v);                  try {                     Log.i("MainActivity", "button clicked");                 } catch (Throwable var5) {                     ViewOnClickListenerAspectj.aspectOf().onViewClickAOP(var2);                     throw var5;                 }                  ViewOnClickListenerAspectj.aspectOf().onViewClickAOP(var2);             }              static {                 ajc$preClinit();             }         });     } }

AspectJ的基本用法就是這樣,SensorsAndroidSDK借助AspectJ插入了Aspect代碼,這是一種靜態的方式。靜態的全埋點方案,本質上是對字節碼進行修改,插入事件上報的代碼。

修改字節碼,除了這種方案之外,還有Android Gradle插件提供的trasform  api(1.5.0版本以上)、ASM、Javassist。在網易樂得的埋點方案,Nuwa熱修復項目都可以見到這些技術的實踐。

3.3 AspectJ相關資料

  • Aspect Oriented Programming in Android:  https://fernandocejas.com/2014/08/03/aspect-oriented-programming-in-android/

  • AOP之AspectJ全面剖析in Android: http://www.jianshu.com/p/f90e04bcb326

  • 滬江開源了一個叫做AspectJX的插件,擴展了AspectJ,除了對src代碼進行AOP,還支持kotlin、工程中引用的jar和aar進行AOP:  https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx

  • 關于 Spring AOP (AspectJ) 你該知曉的一切:  http://blog.csdn.net/javazejian/article/details/56267036

3.4 其他思路

上面介紹的是以AspectJ為代表的 “靜態Hook” 實現方案,有沒有其他辦法可以不修改源代碼,只是在App運行的時候去 “動態Hook”  點擊行為的處理呢?答案是肯定的,在Java的世界,還有反射大法啊,下面看一下怎么實現點擊事件的替換吧。

在 android.view.View.java 的源碼( API>=14 )中,有這么幾個關鍵的方法:

// getListenerInfo方法:返回所有的監聽器信息mListenerInfo ListenerInfogetListenerInfo(){     if (mListenerInfo != null) {         return mListenerInfo;     }     mListenerInfo = new ListenerInfo();     return mListenerInfo; }  // 監聽器信息 static class ListenerInfo{     ... // 此處省略各種xxxListener     /** * Listener used to dispatch click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */     public OnClickListener mOnClickListener;      /** * Listener used to dispatch long click events. * This field should be made private, so it is hidden from the SDK. * {@hide} */     protected OnLongClickListener mOnLongClickListener;      ... } ListenerInfo mListenerInfo;  // 我們非常熟悉的方法,內部其實是把mListenerInfo的mOnClickListener設成了我們創建的OnclickListner對象 public void setOnClickListener(@Nullable OnClickListener l){     if (!isClickable()) {         setClickable(true);     }     getListenerInfo().mOnClickListener = l; }  /** * 判斷這個View是否設置了點擊監聽器 * Return whether this view has an attached OnClickListener. Returns * true if there is a listener, false if there is none. */ public boolean hasOnClickListeners(){     ListenerInfo li = mListenerInfo;     return (li != null && li.mOnClickListener != null); }

通過上面幾個方法可以看到,點擊監聽器其實被保存在了 mListenerInfo.mOnClickListener 里面。那么實現 Hook點擊監聽器  時,只要將這個 mOnClickListener 替換成我們包裝的 點擊監聽器代理對象 就行了。簡單看一下實現思路:

1. 創建點擊監聽器的代理類

// 點擊監聽器的代理類,具有上報點擊行為的功能 class OnClickListenerWrapperimplements View.OnClickListener{     // 原始的點擊監聽器對象     private View.OnClickListener onClickListener;      public OnClickListenerWrapper(View.OnClickListener onClickListener){         this.onClickListener = onClickListener;     }      @Override     public void onClick(View view){         // 讓原來的點擊監聽器正常工作         if(onClickListener != null){             onClickListener.onClick(view);         }         // 點擊事件上報,可以獲取被點擊view的一些屬性         track(APP_CLICK_EVENT_NAME, getSomeProperties(view));     } }

2. 反射獲取一個View的mListenerInfo.mOnClickListener,替換成代理的點擊監聽器

// 對一個View的點擊監聽器進行hook public void hookView(View view) {     // 1. 反射調用View的getListenerInfo方法(API>=14),獲得mListenerInfo對象     Class viewClazz = Class.forName("android.view.View");     Method getListenerInfoMethod = viewClazz.getDeclaredMethod("getListenerInfo");     if (!getListenerInfoMethod.isAccessible()) {         getListenerInfoMethod.setAccessible(true);     }     Object mListenerInfo = listenerInfoMethod.invoke(view);          // 2. 然后從mListenerInfo中反射獲取mOnClickListener對象     Class listenerInfoClazz = Class.forName("android.view.View$ListenerInfo");     Field onClickListenerField = listenerInfoClazz.getDeclaredField("mOnClickListener");     if (!onClickListenerField.isAccessible()) {         onClickListenerField.setAccessible(true);     }     View.OnClickListener mOnClickListener = (View.OnClickListener) onClickListenerField.get(mListenerInfo);          // 3. 創建代理的點擊監聽器對象     View.OnClickListener mOnClickListenerWrapper = new OnClickListenerWrapper(mOnClickListener);          // 4. 把mListenerInfo的mOnClickListener設成新的onClickListenerWrapper     onClickListenerField.set(mListenerInfo, mOnClickListenerWrapper);     // 用這個似乎也可以:view.setOnClickListener(mOnClickListenerWrapper); }

注意,如果是 API<14  的話,mOnClickListener直接是直接以一個Field保存在View對象中的,沒有ListenerInfo,因此反射的次數要更少一些。

3. 對App中所有的View進行Hook

我們在分析的是全埋點,那么怎樣把App里面所有的View點擊都Hook到呢?有兩種方式:

***種:當Activity創建完成后,開始從Activity的DecorView開始自頂向下深度遍歷ViewTree,遍歷到一個View的時候,對它進行hookView操作。這種方式有點暴力,由于這里面遍歷ViewTree的時候用到了大量反射,性能會有影響。

第二種:比***種方式稍微優秀一些,來源是一個Github上的開源庫 AndroidTracker  (Kotlin實現)。他的處理方式是當Activity創建完成后,在DecorView中添加一個透明的View作為子View,在這個子View的onTouchEvent方法中,根據觸摸坐標找到屏幕中包含了這個坐標的View,再對這些View嘗試進行hookView操作。  這種方式比較取巧,首先是拿到了手指按下的位置,根據這個位置來找需要被Hook的View,避免了在遍歷ViewTree的同時對View進行反射。具體實現是在遍歷ViewTree中的每個View時,判斷這個View的坐標是否包含手指按下的坐標,以及View是否Visible,如果滿足這兩個條件,就把這個View保存到一個ArrayList  hitViews。然后再遍歷這個ArrayList里面的View,如果一個View#hasOnClickListeners返回true,那么才對他進行hookView操作。

整體來看,動態Hook的思路用到了反射,難免對程序性能產生影響,如果要采用這種方式實現全埋點方案,還需要好好評估。

四、可視化埋點

4.1 可視化埋點-技術實現

可視化埋點,需要經過兩個步驟,可以由非技術人員操作完成。***步,使用嵌入了Mixpanel/SensorsSDK的App連接后臺,當手機App與后臺同步時,后臺管理界面上會顯示和手機App一樣的界面,用戶可以在管理界面上用鼠標選擇需要監測的元素,設置事件名稱,需要監測的元素屬性等(據說有些SDK的圈選操作是在手機上進行的,不管是什么方式本質上是一樣的,需要保存一份配置到后臺)。第二步,嵌入了SDK的App啟動時,會從服務器獲取到一份配置,再根據這份配置去檢測App中的界面及其元素,滿足配置的條件時向服務器上報事件。下面以Mixpanel、SensorsdataSDK為例,簡單分析一下技術方案的實現。

4.1.1 圈選需要監測的元素,保存配置

1.創建WebSocket連接后臺

采用WebSocket連接是因為要讓手機和后臺長時間保持連接,是一個持續的雙向通信。連接到后臺時,把手機的設備信息發送到后臺。

2.把App界面截圖發送到后臺

創建Socket連接后,在主線程中,對App中啟動的Activity進行掃描,找到界面的RootView(其實是DecorView)。在查找RootView的同時,會對RootView進行截圖,截圖時采用反射調用View類  createSnapshot 方法。

截圖之后,SDK內部會判斷圖片的hash值,如果圖片發生了變化,會采用遞歸的方法深度遍歷Activity的ViewTree,遍歷同時讀取View的屬性(id、top、left、width、height、class名稱、layoutRules等等)。

***,將上面收集到數據發送到連接的后臺,由后臺解析之后,把App界面展示在Web頁面。用戶可以在這個Web頁面圈選需要監測的元素,設置這個元素的時間名稱(event_type和event_name),并保存這個配置。

4.1.2 獲取配置,監測元素的行為,上報事件

1.獲取配置

SDK啟動時,會從服務器拉取一份JSON格式的配置,保存到sharedPreference里,同時SDK會掃描 android.R  文件里面的資源id和資源的name并保存起來。

SDK得到配置之后,解析成JSON對象,讀取 event_bindings 字段,再進一步讀取 events  字段,這個字段下面包含了一個數組,數組的每個元素都描述了一類事件,并包含了這類事件需要監測的元素所在的Activity和元素的路徑。這份配置基本上是這樣的一個結構:

event_bindings: {     events:[         {             target_activity: ""             event_name: ""             event_type: ""             path: [                 {                     prefix:                     view_class:                     index:                     id:                     id_name:                 },                  ...             ]         }     ] }

收到了這份配置之后,SDK會把根據每個event信息,生成一個 ViewVisitor 。 ViewVisitor 的作用就是把 path  數組里面指向的所有View元素都找到,并且根據event_type,給這個View元素設置相應的行為監測器,當這個View發生指定行為時,監測器就會監測到,并上報行為。

生成ViewVisitor之后,SDK內部是以 Map 結構保存它們的,這也比較容易理解。

2.監測元素,上報事件

ViewVisitor 是怎么監測元素的產生的行為呢?答案就是 View.AccessibilityDelegate 。

在Android  SDK里面,AccessibilityService)為我們提供了一系列的事件回調,幫助我們指示一些用戶界面的狀態變化。我們可以派生輔助功能類,進而對不同的AccessibilityEvent進行處理,我們看下AccessibilityEvent里面有哪些事件類型:

/** * Represents the event of clicking on a {@linkandroid.view.View} like * {@linkandroid.widget.Button}, {@linkandroid.widget.CompoundButton}, etc. */ public static final int TYPE_VIEW_CLICKED = 0x00000001;  /** * Represents the event of long clicking on a {@linkandroid.view.View} like * {@linkandroid.widget.Button}, {@linkandroid.widget.CompoundButton}, etc. */ public static final int TYPE_VIEW_LONG_CLICKED = 0x00000002;  /** * Represents the event of selecting an item usually in the context of an * {@linkandroid.widget.AdapterView}. */ public static final int TYPE_VIEW_SELECTED = 0x00000004;  /** * Represents the event of setting input focus of a {@linkandroid.view.View}. */ public static final int TYPE_VIEW_FOCUSED = 0x00000008;  /** * Represents the event of changing the text of an {@linkandroid.widget.EditText}. */ public static final int TYPE_VIEW_TEXT_CHANGED = 0x00000010; ...

以點擊事件 TYPE_VIEW_CLICKED 為例  ,當Activity界面的RootView開始繪制的時候(ViewTreeObserver.OnGlobalLayoutListener的onGlobalLayout回調時),ViewVisitor也會開始尋找指定的View,并給這個View設置新的AccessibilityDelegate。簡單看一下這個新的View.AccessibilityDelegate是怎么寫的:

private class TrackingAccessibilityDelegateextends View.AccessibilityDelegate{ ...             public TrackingAccessibilityDelegate(View.AccessibilityDelegate realDelegate){                 mRealDelegate = realDelegate;             }              public View.AccessibilityDelegategetRealDelegate(){                 return mRealDelegate;             }              ...                          @Override             public void sendAccessibilityEvent(View host,int eventType){                 if (eventType == mEventType) {                     fireEvent(host); // 事件上報                 }                  if (null != mRealDelegate) {                     mRealDelegate.sendAccessibilityEvent(host, eventType);                 }             }              private View.AccessibilityDelegate mRealDelegate;         }         ...

可以看到在SDK的 TrackingAccessibilityDelegate#sendAccessibilityEvent  方法里面,發出了事件上報。

那么View在點擊方法的內部實現里有調用 sendAccessibilityEvent 方法嗎?通過View處理點擊事件時調用的  View.performClick 方法,看一下源碼:

public boolean performClick(){     final boolean result;     final ListenerInfo li = mListenerInfo;     if (li != null && li.mOnClickListener != null) {         playSoundEffect(SoundEffectConstants.CLICK);         li.mOnClickListener.onClick(this);         result = true;     } else {         result = false;     }     sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);     return result; } ... public void sendAccessibilityEvent(int eventType){     if (mAccessibilityDelegate != null) {         mAccessibilityDelegate.sendAccessibilityEvent(this, eventType);     } else {         sendAccessibilityEventInternal(eventType);     } } ... public void setAccessibilityDelegate(@Nullable AccessibilityDelegate delegate){     mAccessibilityDelegate = delegate; }

由此可以見在RootView開始繪制的時候,給View注冊AccessibilityDelegate可以監測到它的點擊事件。

4.2 可視化埋點的難點和問題

上面簡單分析了Mixpanel和SensorsSDK可視化埋點的基本實現,里面還有一個難點需要仔細琢磨,那就是 如何唯一標識App中的一個View?  需要記錄View的哪些信息,如何生成View的唯一ID,保證在不同手機上這些ID是固定的,而且保證App每次啟動,ID也不會變化,同時ID也要能應對一定程度的界面調整。

另外在網上看到有網友提出,setAccessibilityDelegate來監測View的點擊對大多數廠商的機型和版本都是可以的,但是有部分機型是無法成功捕獲監控到點擊事件。從View的標識生成,以及監測原理來講,這個方案的穩定性存在一些疑問。

五、總結

簡單總結一下幾種方案的優缺點和使用場景,在實際應用中多種方式配合使用,平衡效率和可靠性,適合自己的業務才是***的。

如何進行Android埋點技術分析

上述內容就是如何進行Android埋點技術分析,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

神农架林区| 金堂县| 绥滨县| 合水县| 紫金县| 林甸县| 红原县| 凌海市| 合川市| 兴海县| 五寨县| 垫江县| 宁陕县| 富蕴县| 白城市| 大方县| 同德县| 察雅县| 丘北县| 泾源县| 泸水县| 和硕县| 永胜县| 长沙县| 五莲县| 临猗县| 德钦县| 胶州市| 内乡县| 江城| 丹巴县| 普定县| 高密市| 平乡县| 于田县| 福安市| 山西省| 随州市| 苗栗县| 天长市| 牟定县|