作者:路錦(小蘭)
背景:為什麼需要崩潰採集?
系列回顧:在上一篇文章《深度解析 Android 崩潰捕獲原理及從崩潰到歸因的閉環實踐》中,我們深入剖析了崩潰採集的技術內幕——從 Java 層的 UncaughtExceptionHandler 機制,到 Native 層的信號處理與 Minidump 技術,再到混淆堆棧的符號化原理。相信大家對“崩潰是如何被捕獲的”已經有了清晰的認識。
然而,光有理論還不夠。本文將通過復現生產環境案例,當一名 Android 開發同學遇到的線上崩潰問題,該如何通過 RUM 採集的異常數據與上下文進行崩潰的分析與定位,帶你完整體驗崩潰排查的全流程:從收到告警、查看控制枱、分析堆棧、追蹤用户行為,到定位根因。
1.1 案例背景
某 App 發佈了 v3.5.0 版本,主要優化了商品列表的加載性能。然而,版本上線後的第 3 天,團隊開始收到大量用户投訴 App 閃退和崩潰。
問題嚴重性:
- 崩潰率增長 10+ 倍
- 應用商店評分下降
- 用户卸載率上升
最終解決方案:集成了阿里雲 RUM SDK,通過完整的崩潰數據採集,在 2 小時內完成了問題定位。
完整排查流程:從告警到根因定位
2.1 🔔 第一步:收到崩潰告警
數據接入後,由於配置了告警,在線上崩潰率大幅上升時,團隊研發同學會收到告警通知,第一時間關注線上問題。
告警語句參考:
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 應用 → 異常統計。
原圖鏈接: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,這個信息用於我們後續分析用户行為。
查看崩潰堆棧,分析關鍵信息:
- 崩潰發生在
ProductListAdapter.onBindViewHolder()方法的第 50 行 - 錯誤原因:嘗試訪問列表的第 6 個元素(index 5),但列表實際只有 5 個元素
- 這是一個典型的 RecyclerView 數據不一致問題
初步假設:
- 可能是數據更新時機不對
- 可能是多線程併發修改數據
- 可能是用户快速操作導致
但僅憑堆棧還無法確定根因,需要查看用户的具體操作路徑。
2.4 🎯 第四步:追蹤用户行為 - 找到觸發路徑
操作路徑:崩潰詳情頁 → 選擇崩潰對應的會話 ID → 查看該會話 ID 的會話追蹤。
點開會話詳情,我們查看用户的行為路徑,結合崩潰發生的頁面。我們整理出這樣的一個操作路徑。
操作路徑:
- 用户進入 ProductListActivity 頁面
- 快速連續點擊刷新按鈕 3 次,觸發列表異步更新(注:這裏實際發生網絡請求,由於我們是本地復現,使用異步更新)
-
線上請求時序問題:
- 第一次異步請求返回 n 個商品,用户滾動到 6 個
- 後續請求只返回 5 個商品,更新了列表數據
- RecyclerView 還在渲染第 6 個位置,然而數據已經不存在了
- 根本原因:多次異步請求,導致數據競態
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 崩潰大盤總覽
位置:用户體驗監控->體驗看板->異常分析。
異常分析大盤中可以整體看應用的崩潰總覽,包括異常總數、異常趨勢、設備分佈、異常類型、聯網分佈等其他聚合分析結果。
2.5.3 網絡類型分佈
由於實際列表更新操作是由網絡請求返回的,因此我們需要關注線上數據發生崩潰時,用户的聯網類型,在崩潰大盤中查看 v3.5.0 版本的崩潰聯網分佈。
💡 結論:90% 的崩潰發生在 3G/4G 網絡下,WiFi 網絡下崩潰率很低。這印證了網絡(異步請求)是關鍵因素。
2.5.4 設備品牌分佈
在崩潰大盤中查看 v3.5.0 版本崩潰的設備品牌分佈。
💡 結論:所有品牌都受影響,不是特定機型的問題,而是代碼邏輯問題。
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 文件
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 文件。
3.3 驗證符號化
使用符號表文件解析:打開崩潰詳情->異常明細->解析堆棧->選擇對應的符號表文件(native 堆棧使用 .so 文件,java 堆棧使用 .txt 文件。)
點擊確定後即可展示解析後的堆棧。
符號化成功:
- 顯示完整的類名、方法名
- 顯示源文件路徑和行號
- 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...