博客 / 詳情

返回

Android App 穩定性升級:阿里雲 RUM 崩潰採集與用户行為追蹤的全流程實戰

作者:路錦(小蘭)

背景:為什麼需要崩潰採集?

系列回顧:在上一篇文章《深度解析 Android 崩潰捕獲原理及從崩潰到歸因的閉環實踐》中,我們深入剖析了崩潰採集的技術內幕——從 Java 層的 UncaughtExceptionHandler 機制,到 Native 層的信號處理與 Minidump 技術,再到混淆堆棧的符號化原理。相信大家對“崩潰是如何被捕獲的”已經有了清晰的認識。

然而,光有理論還不夠。本文將通過復現生產環境案例,當一名 Android 開發同學遇到的線上崩潰問題,該如何通過 RUM 採集的異常數據與上下文進行崩潰的分析與定位,帶你完整體驗崩潰排查的全流程:從收到告警、查看控制枱、分析堆棧、追蹤用户行為,到定位根因。

1.1 案例背景

某 App 發佈了 v3.5.0 版本,主要優化了商品列表的加載性能。然而,版本上線後的第 3 天,團隊開始收到大量用户投訴 App 閃退和崩潰。

問題嚴重性

  • 崩潰率增長 10+ 倍
  • 應用商店評分下降
  • 用户卸載率上升

最終解決方案:集成了阿里雲 RUM SDK,通過完整的崩潰數據採集,在 2 小時內完成了問題定位。

完整排查流程:從告警到根因定位

2.1 🔔 第一步:收到崩潰告警

數據接入後,由於配置了告警,在線上崩潰率大幅上升時,團隊研發同學會收到告警通知,第一時間關注線上問題。

image

告警語句參考:

app.name: xxx and crash | SELECT diff[1] AS "當前值", diff[2] AS "昨日值", round(diff[3], 4) AS "比值" FROM (SELECT compare(cnt, 86400) AS diff FROM ( SELECT COUNT(*) AS cnt FROM log)) ORDER BY "當前值" DESC

2.2 📊 第二步:查看崩潰概覽 - 鎖定異常類型

操作路徑:控制枱首頁 → 用户體驗監控 → 找到對應的 App 應用 → 異常統計。

image

原圖鏈接:https://img.alicdn.com/imgextra/i4/O1CN01sTmbeh1HuEhF4SKRy_!!6000000000817-2-tps-4684-1262.png

通過分析控制枱展示的異常統計列表,我們發現 IndexOutOfBoundsException 佔據了絕大多數的崩潰,是絕對的主要問題,並且開始大量出現則是 v3.5.0 版本發佈之後。

2.3 🔍 第三步:分析崩潰堆棧 - 初步定位

點擊進入 IndexOutOfBoundsException 詳情頁,深入分析,驗證了我們的想法,這裏可以定位到崩潰版本就是新發布的 v3.5.0,發生的頁面為:ProductListActivity。對應的會話 ID 是:98e9ce65-c51a-40c4-9232-4b69849e5985-01,這個信息用於我們後續分析用户行為。

image

查看崩潰堆棧,分析關鍵信息

  • 崩潰發生在 ProductListAdapter.onBindViewHolder() 方法的第 50 行
  • 錯誤原因:嘗試訪問列表的第 6 個元素(index 5),但列表實際只有 5 個元素
  • 這是一個典型的 RecyclerView 數據不一致問題

image

初步假設

  • 可能是數據更新時機不對
  • 可能是多線程併發修改數據
  • 可能是用户快速操作導致

但僅憑堆棧還無法確定根因,需要查看用户的具體操作路徑。

2.4 🎯 第四步:追蹤用户行為 - 找到觸發路徑

操作路徑:崩潰詳情頁 → 選擇崩潰對應的會話 ID → 查看該會話 ID 的會話追蹤。

image

點開會話詳情,我們查看用户的行為路徑,結合崩潰發生的頁面。我們整理出這樣的一個操作路徑。

操作路徑

  • 用户進入 ProductListActivity 頁面
  • 快速連續點擊刷新按鈕 3 次,觸發列表異步更新(注:這裏實際發生網絡請求,由於我們是本地復現,使用異步更新)
  • 線上請求時序問題

    • 第一次異步請求返回 n 個商品,用户滾動到 6 個
    • 後續請求只返回 5 個商品,更新了列表數據
  • RecyclerView 還在渲染第 6 個位置,然而數據已經不存在了
  • 根本原因:多次異步請求,導致數據競態

image

2.5 🌐 第五步:多維度分析 - 驗證假設

為了進一步確認問題,可以對崩潰數據進行多維度篩選分析,分析故障特徵、確認影響面。

2.5.1 崩潰數據結構

SDK 採集的崩潰數據包含以下核心字段:

{
  "session.id": "session_abc123",         // 會話ID,用於關聯用户行為路徑
  "timestamp": 1699884000000,             // 崩潰發生時間(毫秒時間戳)
  "exception.type": "crash",              // 異常類型
  "exception.subtype": "java",            // 異常子類型
  "exception.name": "java.lang.NullPointerException",  // 異常類型
  "exception.message": "Attempt to invoke virtual method on a null object",  // 異常信息
  "exception.stack": "[{...}]",          // 完整堆棧(JSON數組)
  "exception.thread_id": 1,              // 崩潰線程ID
  "view.id": "123-abc",                    // 崩潰發生頁面ID
  "view.name": "NativeCrashActivity",      // 崩潰發生頁面名稱
  "user.tags:": "{\"vip\":\"true\"}",      // 用户標籤(自定義)
  "properties": "{\"version\":\"2.1.0\"}", // 自定義屬性
  "net.type": "WIFI",                      // 用户網絡類型
  "net.ip": "192.168.1.100",               // 用户客户端IP地址
  "device.id": "123-1234",                // 用户設備ID
  "os.version": 14,                       // 用户系統版本號
  "os.type": "Android"                    // 用户系統類型
}

2.5.2 崩潰大盤總覽

位置:用户體驗監控->體驗看板->異常分析。

異常分析大盤中可以整體看應用的崩潰總覽,包括異常總數、異常趨勢、設備分佈、異常類型、聯網分佈等其他聚合分析結果。

image

2.5.3 網絡類型分佈

由於實際列表更新操作是由網絡請求返回的,因此我們需要關注線上數據發生崩潰時,用户的聯網類型,在崩潰大盤中查看 v3.5.0 版本的崩潰聯網分佈。

image

💡 結論90% 的崩潰發生在 3G/4G 網絡下,WiFi 網絡下崩潰率很低。這印證了網絡(異步請求)是關鍵因素。

2.5.4 設備品牌分佈

在崩潰大盤中查看 v3.5.0 版本崩潰的設備品牌分佈。

image

💡 結論:所有品牌都受影響,不是特定機型的問題,而是代碼邏輯問題

2.5.5 版本對比

除了崩潰大盤,我們仍然可以在日誌探索 tab 頁使用 SQL 自定義分析。

查詢語句:

app.name: xxx and crash | select "app.version", count(*) from log group by "app.version"

操作:對比 v3.4.0 和 v3.5.0 的崩潰率。

版本 崩潰率 IndexOutOfBoundsException 佔比
v3.4.0 0.08% 5%
v3.5.0 1.25% 82.5%

💡結論:問題是 v3.5.0 版本引入的,需要查看這個版本的改動。

2.6 💻 第六步:定位代碼問題

查看問題代碼

打開 ProductListActivity.java,找到刷新邏輯:

private void loadProducts() {
    // ❌ v3.5.0 的改動:使用異步加載優化性能
    new Thread(() -> {
        try {
            // 模擬網絡請求
            List<Product> newProducts = ApiClient.getProducts(currentCategory);
            // ❌ 問題 1:沒有取消前一個請求
            // ❌ 問題 2:直接清空並更新數據,沒有考慮 RecyclerView 正在渲染
            runOnUiThread(() -> {
                productList.clear();              // 💥 危險操作!
                productList.addAll(newProducts);  // 💥 數據更新
                adapter.notifyDataSetChanged();   // 💥 通知刷新
            });
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
}
@Override
public void onBindViewHolder(@NonNull ProductViewHolder holder, int position) {
    // 💥 崩潰點:position 可能超出 products 的範圍
    Product product = products.get(position); //IndexOutOfBoundsException!
    holder.bind(product);
}

找到問題根因!

v3.5.0 的改動目的:優化性能,將網絡請求放到子線程。

引入的問題

1. 沒有取消前一個請求:用户快速點擊刷新時,多個請求同時進行

2. 數據競態:後一個請求返回時,直接清空並更新數據

3. UI 狀態不一致:RecyclerView 正在渲染某個位置,但數據已經變少了

符號化配置:讓堆棧“説人話”

通過前面的排查流程,我們成功定位到了崩潰的根本原因:ProductListAdapter.onBindViewHolder()。

方法在處理數據更新時,存在索引越界問題。但你可能會有一個疑問:我們是如何從混淆後的堆棧中,精確定位到 ProductListAdapter.java:50 這一行代碼的?

在真實的生產環境中,為了保護代碼和優化包體積,發佈到應用商店的 Release 版本都會經過 ProGuard 或 R8 混淆。這意味着控制枱最初看到的崩潰堆棧是這樣的。

java.lang.IndexOutOfBoundsException: Index: 5, Size: 5
    at java.util.ArrayList.get(ArrayList.java:437)
    at com.shop.a.b.c.d.a(Proguard:58)

這就是我們需要符號化的原因。接下來,讓我們看看如何在 RUM 控制枱配置符號化。

3.1 Java/Kotlin 混淆符號化

Step 1:保留 mapping.txt 文件

構建 Release 版本後,mapping.txt 文件位於:

app/build/outputs/mapping/release/mapping.txt

文件內容示例:

com.example.ui.MainActivity -> a.b.c.MainActivity:
    void updateUserProfile(com.example.model.User) -> a
    void onClick(android.view.View) -> b
com.example.model.User -> a.b.d.User:
    java.lang.String userName -> a
    void setUserName(java.lang.String) -> a

Step 2:上傳 mapping 文件到控制枱

1. 登錄雲監控 2.0 控制枱

2. 進入用户體驗監控(RUM)->進入您接入的應用->應用設置->文件管理

3. 點擊符號表文件->上傳文件

4. 上傳 mapping.txt 文件

image

3.2 Native 符號化

構建完成後的目錄中 .so 文件位於:

app/build/intermediates/cxx/release/xxx/obj/
  ├── arm64-v8a/
  │   └── xxx-native.so      ← 包含調試符號
  ├── armeabi-v7a/
  │   └── xxx-native.so
  └── x86_64/
      └── xxx-native.so

Step 3:上傳到控制枱

與 Java mapping 文件類似,在控制枱上傳對應架構的 .so 文件。

image

3.3 驗證符號化

使用符號表文件解析:打開崩潰詳情->異常明細->解析堆棧->選擇對應的符號表文件(native 堆棧使用 .so 文件,java 堆棧使用 .txt 文件。)

image

點擊確定後即可展示解析後的堆棧。

image

符號化成功

  • 顯示完整的類名、方法名
  • 顯示源文件路徑和行號
  • C++ 函數名已還原(非 mangled 狀態)

案例總結:RUM 的關鍵價值

在這次崩潰排查中,RUM 提供了哪些關鍵幫助?

1. 完整的堆棧信息 + 符號化

  • 沒有 RUM:線上應用只能看到混淆後的堆棧,完全不知道是哪裏崩潰
  • 有了 RUM:上傳 mapping 文件後,精確定位到 ProductListAdapter.java:50

2. 用户行為路徑追蹤

  • 沒有 RUM:只知道“用户打開列表就崩潰”,無法復現
  • 有了 RUM:看到完整的操作時間線,發現是“快速點擊刷新多次”觸發

3. 多維度數據分析

  • 沒有 RUM:不知道是哪些用户、什麼環境下崩潰
  • 有了 RUM:

    • 發現 90% 崩潰在 3、4G 網絡下(網絡延遲是關鍵)
    • 所有機型都受影響(排除硬件問題)
    • v3.5.0 才開始出現(鎖定版本改動)

4. 實時告警 + 量化影響

  • 沒有 RUM:依賴用户投訴,發現滯後
  • 有了 RUM:第一時間收到告警,立即開始問題排查

應用的穩定性是用户體驗的基石。通過系統化的崩潰採集與分析,開發團隊能夠從“被動響應”轉變為“主動預防”,持續提升應用質量,贏得用户信任。阿里雲 RUM 針對 Android 端實現了對應用性能、穩定性、和用户行為的無侵入式採集 SDK,可以參考接入文檔 [ 1] 體驗使用。除了 Android 外,RUM 也支持 Web、小程序、iOS、鴻蒙等多種平台監控分析,相關問題可以加入“RUM 用户體驗監控支持羣”(釘釘羣號:67370002064)進行諮詢。

相關鏈接:

[1] 接入文檔

https://help.aliyun.com/zh/arms/user-experience-monitoring/ac...

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.