<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      告別手動埋點!Android 無侵入式數據采集方案深度解析

      作者:路錦(小蘭)

      Android 應用數據采集背景

      在移動應用開發領域,對應用性能(APM)和用戶體驗的實時監控至關重要。傳統的監控方案通常要求開發者在代碼中手動添加和初始化 SDK,并在需要監控的業務邏輯處(如網絡請求、頁面跳轉、用戶點擊等)手動調用埋點代碼。

      這種方式存在諸多痛點:

      • 侵入性強:監控代碼與業務代碼高度耦合,增加了代碼的復雜度和維護成本。
      • 工作量大:對于龐大的應用,手動埋點耗時耗力,且容易遺漏關鍵的監控點。
      • 難以維護:業務邏輯的頻繁變更可能導致埋點代碼失效或需要同步修改,增加了出錯的風險。
      • 接入成本高:新項目或新團隊成員需要花費時間學習和理解埋點規范。

      image

      為了解決以上問題,實現監控能力的自動化、全面化和降低接入成本,無侵入式的插樁方案應運而生。其核心目標是在不修改應用源碼的情況下,通過在編譯打包過程中自動注入監控探針,實現對應用行為的全面監控,將開發者從繁瑣的埋點工作中解放出來。

      核心挑戰與關注點

      在設計和實現一套穩定、高效的無侵入插樁方案時,我們必須面對并解決以下核心挑戰:

      image

      1)Android 生態的碎片化挑戰

      Android 系統的開放性導致其生態存在嚴重的碎片化問題,這在構建工具層面尤為突出。Android Gradle 插件(AGP)版本迭代迅速,核心編譯 API 頻繁變更(例如從 Transform API 到 Instrumentation API 的遷移)。插樁方案必須能夠動態適配不同的 AGP 版本,否則將無法在開發者的多樣化環境中正常工作。

      2)第三方插件的兼容性與沖突風險

      市面上的 APM 或功能增強插件(如其他監控工具、熱修復框架等)大多采用類似的字節碼插樁技術。如果我們的插樁方案與其他插件在同一位置修改了同一段代碼,極易引發構建錯誤或運行時沖突。因此,必須設計一套機制來避免“重復插樁”,并盡可能地與其他插件和平共存。

      3)插樁代碼的健壯性與獨立性

      通過插件注入到用戶代碼中的探針必須具備極高的健壯性和獨立性。一個常見且致命的問題是:如果用戶在項目中應用了插樁插件,但忘記在代碼中初始化主 SDK,那么注入的探針代碼在調用 SDK 功能時可能會因為依賴未就緒而導致空指針(NullPointerException)等嚴重崩潰。插樁方案必須保證即使在主 SDK 未啟動的情況下,應用也不會崩潰。

      Android 無侵入式采集方案探討

      業界主流的無侵入插樁方案主要圍繞在編譯期對代碼進行修改,采集原理均基于 AOP 思想,AOP 的思想主張將“橫切關注點”從業務邏輯中抽離出來,獨立地封裝到一個被稱為“切面”(Aspect)的模塊中,然后通過聲明的方式,告訴程序應該在“什么時機”、“什么地方”去執行這些切面中的邏輯,而不需要去修改業務邏輯的源碼。

      應用數據采集場景分析

      Android 端的無侵入采集種類繁多,但核心思想都是通過自動化手段在不修改業務代碼的前提下,捕獲應用運行時的各種事件和數據。按照 Android 應用常見的采集場景分類,我們分別探討每種場景對應的方案選型。

      1. 用戶行為與頁面采集

      這類采集的目標是了解用戶如何與 App 交互,以及頁面的生命周期。

      • 頁面(Activity/Fragment)生命周期采集

        • 技術方案:
          • Activity:Application.registerActivityLifecycleCallbacks。
          • Fragment:AndroidX 可用生命周期回調;老版 android.app.Fragment 常用字節碼插樁在 onResume/onPause/onViewCreated 等方法前后注入。
        • 采集數據:頁面瀏覽路徑、頁面加載時長、PV/UV 統計。
      • 用戶交互事件(點擊、滑動等)

        • 技術方案:主流方案是字節碼插樁,例如通過 ASM 操作字節碼。
          • 代理監聽器:插樁修改 setOnClickListener 等設置監聽器的方法,將其中的監聽器替換為一個代理監聽器。代理類在執行原始邏輯前后加入采集代碼。這種方式可以精確采集到控件信息。
          • Hook 方法:通過字節碼插樁技術,在編譯期直接向處理點擊事件的方法中注入采集代碼來實現。
        • 采集數據:控件點擊事件(Action)、控件的標識(ID、文本)、關聯頁面。

      2. 網絡請求監控

      目標是采集 App 發出的所有 HTTP/HTTPS 請求的性能和成功率。

      • 技術方案:同樣以字節碼插樁為主,針對不同的網絡庫進行 Hook。
        • OkHttp:這是目前最主流的網絡庫。可以通過插樁  OkHttpClient.Builder.build() 方法,在其中添加一個自定義的攔截器來獲取請求的全部信息,并計算請求性能。
        • HttpURLConnection:這是 Android 原生的網絡請求方式。通常插樁 URL.openConnection() 方法,將其返回的 HttpURLConnection 對象替換為一個代理對象,從而在代理類中監控回調方法,實現數據采集。
        • 其他網絡庫:如 Retrofit 等,它們的底層邏輯通常也是基于OkHttp 或HttpURLConnection。
        • 采集數據:URL、請求方法、HTTP 狀態碼、請求耗時(DNS、TCP、SSL、總耗時等)、請求和響應體大小、TraceID(用于分布式鏈路追蹤)。

      3. 應用性能監控

      • 應用啟動耗時

        • 技術方案:
          • 冷啟動、熱啟動:通常通過 Android API 采集。
      • UI 卡頓與長任務

        • 技術方案:
          • Looper 監控:通過 Looper.getMainLooper().setMessageLogging() 設置一個自定義的 Printer,可以監控到主線程 Looper 處理每個 Message 的開始和結束。如果處理單個 Message 耗時過長,即可判定為一次卡頓或長任務,并抓取主線程堆棧。
      • ANR (Application Not Responding)

        • 技術方案:通用做法是啟動一個獨立的“看門狗”線程,該線程定期向主線程的 Looper 發送一個任務。如果在規定時間(如 4-5 秒)內該任務沒有被執行,就認為主線程被阻塞,此時“看門狗”線程會抓取主線程的堆棧信息,作為 ANR 日志上報。

      4. 崩潰監控

      • Java/Kotlin 崩潰

        • 技術方案:使用 Thread.setDefaultUncaughtExceptionHandler() 設置一個全局的未捕獲異常處理器。當應用崩潰時,這個處理器會被調用,SDK 可以在這里捕獲異常信息、堆棧、線程狀態等,保存后上報。
      • Native (C/C++) 崩潰

        • 技術方案:通過 JNI 實現。使用 Linux 的 Signal 信號處理機制,注冊對 SIGSEGV, SIGABRT, SIGILL 等致命信號的監聽。當 Native 代碼崩潰觸發這些信號時,信號處理器被回調。在處理器中,可以記錄下崩潰現場保存為文件,待下次 App 啟動時上報。

      5. WebView監控

      • 技術方案:核心是 JS 探針注入。
        • 通過字節碼插樁 Hook WebView 的相關方法,插入 JS 采集探針實現采集。

      小結:根據采集場景分析,我們發現在 Android 無侵入式采集中,最需要關注的技術是字節碼插樁。

      字節碼插樁技術

      • 技術介紹:這是目前 Android 領域最主流和最強大的無侵入技術。它利用 Android Gradle 插件(AGP)在編譯過程中提供的 API(如 Transform API 或新的 Instrumentation API),在 .class文件被編譯成 .dex 文件之前,對其字節碼進行掃描和修改。其中,ASM 是一個高性能、輕量級的 Java 字節碼操作和分析框架。它提供了豐富的 API,可以像操作對象一樣對類的結構、字段、方法和指令進行精細化的增刪改查。

      • 原理:

      image

      1.  通過插件注冊一個在編譯構建階段執行的任務。
      2.  該任務遍歷項目源碼和所有依賴庫(jar/aar)中的 `.class` 文件。
      3.  使用 ASM 的 `ClassReader` 讀取每個類的字節碼。
      4.  通過自定義的 `ClassVisitor` 和 `MethodVisitor` 訪問類和方法的結構。
      5.  在 `MethodVisitor` 中找到需要注入代碼的目標位置(如方法入口、方法出口、或者某個特定指令前后),并插入新的字節碼指令。
      6.  使用 `ClassWriter` 將修改后的字節碼寫回,替換原文件。
      
      • 優點:控制粒度最細,功能最為強大,幾乎可以實現任意邏輯的注入。性能開銷極低,是實現高性能監控 SDK 的首選方案。

      • 構建 API 演進與兼容

        • Transform API:AGP 8 起移除。
        • Instrumentation API:AGP 7 引入,AGP 8 強烈推薦且基本強制使用。
        • 插件需動態選擇:優先使用 Instrumentation API;舊環境降級到 Transform。

      無侵入式插樁方案實踐

      基于上述Android無侵入式采集方案分析,我們這里使用字節碼插樁技術,以點擊行為采集場景為例,進行一次完整的無侵入式采集方案實踐。

      核心思想

      章節一中我們提到了無侵入式采集需要面臨的挑戰,這里我們整理了以下三個核心思想,來解決上述問題。

      AGP 版本動態適配策略

      為了適應 Android Gradle 插件的快速迭代,在插件開發中需要對新舊版本的 AGP 做兼容處理,以不同版本的AGP兼容性特點為例:

      • 老版本AGP (Legacy): 插件會使用 AGP 舊版的  TransformAPI 來處理字節碼的轉換邏輯。
      • AGP 7+: 插件會采用 Google 官方推薦的 Instrumentation API 來負責實現新版 API 的對接,這種方式更高效、更穩定。

      在插件入口時可以在運行時動態檢測 AGP 版本,并選擇相應的實現,對開發者完全透明。這里我們提供一個基于不兼容 API 的適配策略。

      MyApmPluginpublic class MyApmPlugin implements Plugin<Project> {
         @Override
          public void apply(Project project) {
              boolean hasAsmFactory = classExists("com.android.build.api.instrumentation.AsmClassVisitorFactory");
              boolean hasTransform = classExists("com.android.build.api.transform.Transform");
              if (hasAsmFactory) {
                  // AGP 7+,使用 Instrumentation API(推薦)
                  new Agp7PlusImpl(project).init();
              } else if (hasTransform) {
                  // 老版本,使用 Transform API
                  new LegacyTransformImpl(project).init();
              } else {
                  project.getLogger().warn("No supported AGP API found. Plugin disabled.");
              }
          }
      }
      

      兼容性設計,避免插件沖突

      為了最大限度地兼容第三方插件并防止沖突,我們提供了以下方案:

      • 黑名單:跳過系統包、常見 APM/熱修復/加固框架、自身 SDK。
      • 白名單:可選,只處理應用/業務相關包,最大程度降低誤傷與沖突。
      • 冪等插樁:避免重復注入,例如 tag 標記、instanceOf 判斷。
      • 注解式控制:可選,支持 @NoTrack、@TrackIgnore 注解,編譯期間掃描后跳過特定類/方法,給業務兜底控制權。
      • 插樁失敗回退:單類插樁失敗時,記錄日志并回退為原始字節碼,構建繼續。

      這里是一個黑名單過濾樣例代碼:

      public class ClassInstrumentChecker {
          private static final List<String> BLACKLISTED_PREFIXES = Arrays.asList(
              // 常見系統庫、協程等應該避免插樁
              "java/",
              "javax/",
              "kotlin/", 
              "kotlinx/",
              "android/",
              "androidx/",
              "com/my/apm/sdk/" // 自身 SDK,避免遞歸處理
              // 其他 APM 或性能監控產品等
              "com/networkbench/",
              "com/sensorsdata/",
              "com/tencent/qapmsdk/",
              // 常見熱修復或加固框架
              "com/tencent/tinker/",
              "com/taobao/sophix/",
              // 自身 SDK,避免重復處理
              "com/my/apm/sdk/"
          );
          /**
           * 檢查一個類是否應該被插樁。
           * @param className 類的名稱 (e.g., "com/example/myapp/MyClass")
           * @return 如果應該被插樁,返回 true;否則返回 false。
           */
          public static boolean shouldInstrument(String className) {
              // 排除 R 文件和 BuildConfig
              if (className.contains("/R$") || className.endsWith("/R") || className.endsWith("/BuildConfig")) {
                  return false;
              }
              for (String prefix : BLACKLISTED_PREFIXES) {
                  if (className.startsWith(prefix)) {
                      return false; // 命中黑名單,跳過
                  }
              }
              return true; // 未命中,可以插樁
          }
      }
      

      安全插樁,確保代碼獨立與穩定

      這是保障方案健壯性的核心。我們秉持“最少侵入”和“絕對安全”的原則,確保注入的代碼穩定且無副作用。

      • 不替換原生邏輯: 我們的插樁始終是在原生方法邏輯的“之前”或“之后”進行補充,而不是替換。例如,在監聽網絡請求時,我們會先調用 SDK 的追蹤方法,然后通過 super.visitMethodInsn() 繼續執行原生的網絡調用指令,保證應用原有功能不受任何影響。
      • 不引入第三方依賴: 注入的字節碼指令極其精簡,僅包含對我們自身 SDK 中特定靜態方法的調用(如 TrackInstrument.trackViewOnClick(...)),不引入任何新的外部庫依賴,保持了插樁點的純凈性。
      • 探針代碼獨立運行與異常隔離: 這是解決“SDK 未初始化”問題的關鍵。所有被注入的探針最終調用的 SDK 工具類(如 TrackInstrument)內部都遵循了嚴格的防御性編程。在該工具類的入口處,會首先檢查主 SDK 是否已成功初始化。插樁部分發生異常時不影響原始業務邏輯,未初始化時所有插樁代碼會立即靜默返回,不執行任何實質性操作。 確保了即使在極端情況下注入的探針也不會引發任何崩潰。
      • Kotlin 與 Jetpack Compose 插樁補充:
        • 內聯函數 (inline):inline 函數體在編譯期被直接復制到調用處,可能改變最終方法布局與調用棧,插樁目標應是其內部調用的非內聯方法,而非 inline 函數本身。
        • Jetpack Compose 點擊事件 (Modifier.clickable):通常通過 Modifier.clickable 實現,需另行在 Compose Runtime 層或特定包裝函數處插樁(或在 UI Toolkit 層提供可選的輕量擴展,而非硬插樁)。
        • Lambda 表達式的實現差異:Lambda 的字節碼實現方式不唯一。為兼容低版本安卓,編譯器可能將其“脫糖” (Desugar) 為匿名內部類,而非現代的 invokedynamic 實現。兩種模式生成的方法簽名(類名、方法名、是否靜態)完全不同,插樁方案必須兼容這兩種情況,以防因簽名不匹配而失效。

      這里提供一個插樁方法樣例,給 OnClick 事件插入我們需要的日志采集代碼。

      public class OnClickMethodVisitor extends AdviceAdapter {
          public OnClickMethodVisitor(MethodVisitor mv, int access, String name, String desc) {
              super(Opcodes.ASM9, mv, access, name, desc);
          }
          @Override
          protected void onMethodEnter() {
              super.onMethodEnter();
              // 在 onClick(Landroid/view/View;)V 方法的入口處插入代碼
              mv.visitVarInsn(Opcodes.ALOAD, 1); // 加載第一個參數 (View 對象) 到操作數棧
              // 調用我們自己的靜態工具方法來處理點擊事件
              mv.visitMethodInsn(
                  Opcodes.INVOKESTATIC, // 靜態方法調用
                  "com/my/apm/sdk/TrackInstrument", // 包含追蹤方法的類
                  "trackViewOnClick", // 方法名
                  "(Landroid/view/View;)V", // 方法描述符
                  false // 非接口方法
              );
          }
      }
      
      public class TrackInstrument {
          public static void trackViewOnClick(View view) {
              try {
                  // 核心安全設計:檢查 SDK 是否已初始化
                  if (!MyApmAgent.isInitialized() || view == null) {
                      return; // 如果未初始化,則靜默返回,不執行任何操作
                  }
                  // 如果已初始化,則執行正常的事件采集邏輯
                  String viewId = getViewId(view);
                  MyApmAgent.get().logUserAction("click", viewId);
              } catch (Throwable ignored) {
                  // 完全隔離異常,避免影響業務
              }
          }
      }
      

      插樁實踐

      結合上述核心思想,我們完整地串聯起一個 onClick 點擊數據的采集方案。方案的核心是一個自定義的 Gradle 插件,當 Android 應用集成此插件后,它會自動注入到應用的構建流程中,在編譯期完成代碼的自動化注入。

      無論使用哪種 AGP API,核心的字節碼修改邏輯都由 ASM 庫驅動,整體流程如下:

      1. 遍歷 Class 文件

      插件在執行時會獲取到項目中的所有 .class 文件,包括源碼編譯的類和第三方庫中的類。

      // 代碼樣例: 在 Gradle Transform 中遍歷輸入文件
      @Override
      public void transform(TransformInvocation transformInvocation) {
          Collection<TransformInput> inputs = transformInvocation.getInputs();
          TransformOutputProvider outputProvider = transformInvocation.getOutputProvider();
          for (TransformInput input : inputs) {
              // 遍歷 Jar 包
              for (JarInput jarInput : input.getJarInputs()) {
                  File srcJar = jarInput.getFile();
                  File destJar = outputProvider.getContentLocation(...);
                  processJar(srcJar, destJar);
              }
              // 遍歷目錄
              for (DirectoryInput directoryInput : input.getDirectoryInputs()) {
                  File srcDir = directoryInput.getFile();
                  File destDir = outputProvider.getContentLocation(...);
                  processDirectory(srcDir, destDir);
              }
          }
      }
      

      2. ASM 分析與修改

      • 每個類文件都會被 ClassAdapter 訪問。該類是一個 ClassVisitor,它會首先進行過濾,通過 isClassShouldInstrument 方法中的黑名單,跳過對系統庫、其他 APM 產品、SDK 自身以及一些已知會產生沖突的第三方庫的插樁,以保證穩定性和兼容性。
      • 對于需要處理的類,核心插樁任務交由 MyMethodAdapter(一個 AdviceAdapter 的子類)完成。它會遍歷類中的每一個方法。
      // 代碼樣例: ClassVisitor 和 MethodVisitor 的責任鏈
      public class MyClassAdapter extends ClassVisitor {
          private String className;
          public MyClassAdapter(ClassVisitor classVisitor) {
              super(Opcodes.ASM9, classVisitor);
          }
          @Override
          public void visit(int version, int access, String name, ...) {
              super.visit(version, access, name, ...);
              this.className = name;
          }
          @Override
          public MethodVisitor visitMethod(int access, String name, String descriptor, ...) {
              MethodVisitor mv = super.visitMethod(access, name, descriptor, ...);
              // 檢查此類是否在黑名單中
              if (!ClassInstrumentChecker.shouldInstrument(className)) {
                  return mv; // 在黑名單中,返回原始 MethodVisitor,不處理
              }
              // 如果需要處理,則返回我們自定義的 MethodVisitor
              return new MyMethodAdapter(mv, access, name, descriptor);
          }
      }
      

      3. 采集探針注入 (Hook)

      • MyMethodAdapter 的 onMethodEnter 方法會在方法體的最開始處插入代碼。
      • MyHookConfig.java 文件中預定義了所有需要 Hook 的目標方法,例如點擊行為的 onClick 方法等。
      • 當 MyMethodAdapter 訪問到這些目標方法時,就會在方法開頭插入對 TrackInstrument 中對應追蹤方法的調用(如 trackViewOnClick),從而實現對頁面生命周期、用戶點擊、菜單選擇等事件的自動采集。
      • Lambda 表達式處理:通過 visitInvokeDynamicInsn 指令對 Java 8 的 Lambda 表達式進行了特殊處理,能夠準確識別出作為監聽器實現的 Lambda 表達式(如 view.setOnClickListener(v -> ...)),并對其進行正確的插樁。
      public class OnClickAdviceAdapter extends AdviceAdapter {
        protected OnClickAdviceAdapter(MethodVisitor mv, int access, String name, String desc) {
        super(Opcodes.ASM9, mv, access, name, desc);
      }
      @Override protected void onMethodEnter() {
        // 加載參數 View(index=1)
      visitVarInsn(ALOAD, 1);
      // 調用靜態方法 TrackInstrument.trackViewOnClick(View)
      visitMethodInsn(INVOKESTATIC,
                      "com/my/apm/sdk/TrackInstrument",
                      "trackViewOnClick",
                      "(Landroid/view/View;)V",
                      false);
      }
      }
      
      // 代碼樣例: MethodVisitor 的實現, 包含 Lambda 處理
      public class MyMethodAdapter extends AdviceAdapter {
          private final String methodNameDesc;
          private final String className;
          public MyMethodAdapter(MethodVisitor mv, int access, String name, String desc, String className) {
              super(Opcodes.ASM9, mv, access, name, desc);
              this.methodNameDesc = name + desc;
              this.className = className;
          }
          @Override
          protected void onMethodEnter() {
              super.onMethodEnter();
              // 檢查當前方法是否為普通方法或已被標記的 Lambda 方法
              MyHookConfig.HookCell hookCell = MyHookConfig.HOOK_METHODS.get(methodNameDesc);
              if (hookCell == null) {
                  hookCell = MyHookConfig.LAMBDA_METHODS_TO_HOOK.get(methodNameDesc);
              }
              if (hookCell != null) {
                  // 注入探針代碼... (此部分邏輯見核心思想中的樣例)
              }
          }
          @Override
          public void visitInvokeDynamicInsn(String name, String descriptor, Handle bsm, Object... bsmArgs) {
              super.visitInvokeDynamicInsn(name, descriptor, bsm, bsmArgs);
              try {
                  // 檢查是否為我們關心的 Lambda 表達式, 例如 OnClickListener
                  String samMethodDesc = ((Type) bsmArgs[0]).getDescriptor();
                  if ("(Landroid/view/View;)V".equals(samMethodDesc)) {
                      // 獲取 Lambda 的方法體實現信息
                      Handle implMethodHandle = (Handle) bsmArgs[1];
                      String lambdaBodySignature = implMethodHandle.getName() + implMethodHandle.getDesc();
                      // 將該 Lambda 的方法體標記為需要插樁,值為 onClick 的 HookCell
                      MyHookConfig.LAMBDA_METHODS_TO_HOOK.put(lambdaBodySignature, MyHookConfig.HOOK_METHODS.get("onClick(Landroid/view/View;)V"));
                  }
              } catch (Exception e) {
                  // ignore
              }
          }
      }
      
      // 代碼樣例: Hook 配置類
      public class MyHookConfig {
          public static final Map<String, HookCell> HOOK_METHODS = new HashMap<>();
          // 用于存儲被識別出的、需要被插樁的 Lambda 方法體
          public static final Map<String, HookCell> LAMBDA_METHODS_TO_HOOK = new ConcurrentHashMap<>();
          static {
              HOOK_METHODS.put("onClick(Landroid/view/View;)V", new HookCell("trackViewOnClick", "(Landroid/view/View;)V"));
          }
          // ... 其他配置
      }
      

      4. 生成新類

      所有修改完成后,ClassWriter 會生成新的字節碼,替換原有的 .class 文件。這些被注入了監控探針的類文件最終會被打包進 APK 中,在應用運行時自動執行監控邏輯。

      // 代碼樣例: ASM 生成新字節碼的核心邏輯
      public byte[] processClass(InputStream classInputStream) throws IOException {
          ClassReader classReader = new ClassReader(classInputStream);
          // ClassWriter 會在責任鏈的末端,負責將所有修改寫入字節碼
          ClassWriter classWriter = new ClassWriter(classReader, ClassWriter.COMPUTE_MAXS);
          // 啟動訪問者鏈,MyClassAdapter 是我們自定義的訪問器
          classReader.accept(new MyClassAdapter(classWriter), ClassReader.EXPAND_FRAMES);
          // 返回包含所有修改的新字節碼
          return classWriter.toByteArray();
      }
      

      總結

      本文通過探討 Gradle 插件 + AGP API + ASM 字節碼插樁的插樁方案,實現了一套對業務代碼零侵入、易于集成、可安全運行的自動化監控采集方案。阿里云 RUM 針對 Android 端實現了對應用性能、穩定性、和用戶行為的無侵入式采集 SDK。可以參考接入文檔 [ 1] 體驗使用。相關問題可以加入“RUM 用戶體驗監控支持群”(釘釘群號:67370002064)進行咨詢。

      相關鏈接:

      [1] 接入文檔

      https://help.aliyun.com/zh/arms/user-experience-monitoring/access-to-android-applications

      posted @ 2025-10-27 14:47  阿里云云原生  閱讀(20)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 野花香视频在线观看免费高清版| 亚洲天堂在线观看完整版| 国产三级精品片| 色婷婷狠狠久久综合五月| 2019香蕉在线观看直播视频| 亚洲精品一区| 自拍偷自拍亚洲精品熟妇人| 亚洲精品麻豆一二三区| 午夜毛片不卡免费观看视频| 在线看av一区二区三区| 国产精品国产三级国快看| 狠狠亚洲狠狠欧洲2019| 国产AV无码专区亚洲AV漫画| 亚洲人成网站观看在线观看| 精品无人码麻豆乱码1区2区| 国产精品高清一区二区三区| 中文字幕日韩人妻一区| 深夜av在线免费观看| 99国产精品白浆在线观看免费 | 人人超人人超碰超国产| 国产福利视频区一区二区| 亚洲成av人片无码天堂下载| 国产又色又爽又黄的在线观看| 久久91精品牛牛| 国产成人免费| 国产精品视频中文字幕| 国产白丝jk捆绑束缚调教视频| 色悠悠久久精品综合视频| 亚洲精品中文字幕二区| 国产亚洲综合一区二区三区| 在线亚洲高清揄拍自拍一品区| 国产精品污一区二区三区| 中文字幕亚洲国产精品| 国产高清在线不卡一区| av激情亚洲男人的天堂| 亚洲国产性夜夜综合| 精品91在线| 无翼乌口工全彩无遮挡h全彩| 无码av永久免费专区麻豆| 国产精品无码av不卡| 国产精品入口中文字幕|