1. 背景:性能怪獸與消失的掉幀
在高性能設(shè)備(如 Samsung S22,搭載驍龍 8 Gen 1)上,用戶對(duì) UI 的流暢度有著極高的預(yù)期。然而,在處理包含 4000+ PDF 文件的極端場(chǎng)景時(shí),簡(jiǎn)單的文件掃描邏輯往往會(huì)導(dǎo)致明顯的 UI 卡頓甚至 ANR。
經(jīng)過(guò) Trace 分析,我們發(fā)現(xiàn)卡頓并非硬件性能不足,而是典型的主線程負(fù)載過(guò)重。在 120Hz 刷新率下,每一幀的繪制時(shí)間僅為 8.3ms。任何在 UI 鏈路上執(zhí)行的磁盤(pán) IO(如 File.exists())或 級(jí)別的計(jì)算,都是對(duì)硬件性能的褻瀆。
2. 核心痛點(diǎn)分析
傳統(tǒng)的全盤(pán)物理掃描方案在現(xiàn)代 Android 開(kāi)發(fā)中面臨三大挑戰(zhàn):
-
Google Play 合規(guī)性:
MANAGE_EXTERNAL_STORAGE權(quán)限審核極其嚴(yán)格,盲目申請(qǐng)會(huì)導(dǎo)致應(yīng)用下架。 - IO 瓶頸:在大規(guī)模文件目錄下,遞歸遍歷(BFS/DFS)會(huì)產(chǎn)生巨大的 IO 消耗和 CPU 峰值。
-
數(shù)據(jù)轉(zhuǎn)換黑洞:在
Flow的轉(zhuǎn)換操作符(如map)中進(jìn)行數(shù)據(jù)加工,會(huì)導(dǎo)致 UI 刷新與數(shù)據(jù)計(jì)算在主線程競(jìng)爭(zhēng)資源。
3. 二級(jí)掃描策略:權(quán)衡隱私與深度
為了兼顧合規(guī)性與功能完整性,我們?cè)O(shè)計(jì)了分層掃描模型:
第一級(jí):基礎(chǔ)掃描(Default / Passive)
-
核心源:
MediaStore+SAF (Storage Access Framework)手動(dòng)導(dǎo)入。 - 原理:利用系統(tǒng)級(jí)索引數(shù)據(jù)庫(kù)。
- 優(yōu)點(diǎn):秒開(kāi)級(jí)別的響應(yīng)速度,無(wú)需特殊權(quán)限,符合 Google Play 隱私規(guī)范。
- 局限:存在索引延遲,無(wú)法觸達(dá)某些非標(biāo)準(zhǔn)目錄。
第二級(jí):深度掃描(Optional / Active)
- 核心源:全盤(pán)物理遞歸遍歷(BFS)。
- 開(kāi)啟條件:用戶主動(dòng)在設(shè)置中開(kāi)啟并授予“所有文件訪問(wèn)權(quán)限”。
-
職責(zé):作為第一級(jí)的補(bǔ)充,通過(guò)底層的物理掃描補(bǔ)全
MediaStore可能漏掉的文件。
4. 架構(gòu)實(shí)現(xiàn):異步預(yù)處理管道
為了徹底消除 4000 個(gè)文件帶來(lái)的卡頓,我們引入了數(shù)據(jù)成品化(Data Ready)機(jī)制。
邏輯解耦:讀寫(xiě)分離
-
寫(xiě)入側(cè)(Write Side):無(wú)論是來(lái)自
MediaStore還是物理遍歷,原始文件數(shù)據(jù)在進(jìn)入數(shù)據(jù)庫(kù)(Room)前,必須在后臺(tái)線程(Dispatchers.IO/Default)完成所有重型加工。- 執(zhí)行
file.exists()校驗(yàn)。 - 計(jì)算并格式化
sizeLabel(如 "1.2 MB")和dateLabel。
- 執(zhí)行
-
讀取側(cè)(Read Side):UI 觀察的
Flow<List<PdfFile>>僅包含已經(jīng)格式化好的字符串。
線程調(diào)度優(yōu)化
嚴(yán)禁在 Flow.map 或 Flow.combine 等操作符中執(zhí)行磁盤(pán) IO。通過(guò) .flowOn(Dispatchers.Default) 將數(shù)據(jù)加工邏輯與 UI 收集邏輯物理隔離。
5. 極限測(cè)試結(jié)論
在 4000+ PDF 文件的模擬環(huán)境下:
-
重構(gòu)前:由于在主線程 Flow 轉(zhuǎn)換中執(zhí)行
emergencyFilter(包含文件 IO),Tab 切換動(dòng)畫(huà)出現(xiàn)明顯撕裂,掉幀嚴(yán)重。 - 重構(gòu)后:采用“入庫(kù)即成品”策略,UI 僅負(fù)責(zé)渲染預(yù)存的字符串。在 S22 上,列表滾動(dòng)與 Tab 切換完全回歸 120Hz 滿幀表現(xiàn)。
6. 總結(jié)與展望
高性能不是“跑得快”,而是“路不堵”。通過(guò)二級(jí)掃描策略解決合規(guī)與深度問(wèn)題,通過(guò)異步預(yù)處理管道解決 UI 性能問(wèn)題,我們將一個(gè)底層的文件操作模塊升級(jí)為了一個(gè)高性能的系統(tǒng)組件。
這種架構(gòu)設(shè)計(jì)不僅適用于 PDF 管理,在后續(xù)的音頻處理(EchoFlow)或 AI 知識(shí)庫(kù)構(gòu)建中,都可以作為通用的基礎(chǔ)設(shè)施進(jìn)行復(fù)用。在 Android 碎片化的生態(tài)中,這種“分層防御 + 數(shù)據(jù)成品化”的思路,是實(shí)現(xiàn)商用級(jí)絲滑體驗(yàn)的唯一路徑。