摘要:目前,FlutterCodeX在閒魚App即將上線,結合客戶端Android、iOS代碼覆蓋率數據,有效地推動廢棄業務下線,助力包體瘦身,對工程健康做長效監控與改善。針對類級別編譯時代碼插粧,運行時後臺數據聚合,進行數據採集上報,獲得最終代碼覆蓋率數據,推動廢棄業務下線,達到包體瘦身,對工程健康做長效監控與改善。

背景

近年來,閒魚舊業務在Flutter架構升級下,大量頁面通過Flutter開發實現。業務不斷迭代,包體積也隨之增大,閒魚Android、iOS安裝包大小較去年有較大增加,其中,Flutter在閒魚包體積中佔比20%,閒魚開發逐步需要考慮進行Flutter側工程治理。Flutter官方也在爲包大小不斷努力,致力於降低打包產物的大小,但仍未有成熟方案。因此現階段,我們可以考慮如何將無效代碼下線。

通過人工梳理的方式,依賴於開發人員的業務熟悉程度,難免疏漏。我們需要有準確的的線上代碼覆蓋率,作爲數據依據,推動業務進行行之有效的代碼下線。

本文爲您介紹,Flutter的線上代碼覆蓋率解決方案——FlutterCodeX。針對類級別編譯時代碼插粧,運行時後臺數據聚合,進行數據採集上報,獲得最終代碼覆蓋率數據,推動廢棄業務下線,達到包體瘦身,對工程健康做長效監控與改善。

插樁方案 探索

在線上代碼覆蓋率的統計中,問題的難點主要在於,如何準確判斷類,是否被調用過?一般人會馬上可以想到,只需要在每個類初始化時,加入一段代碼,標記該類已經被調用,最快的就是構建函數中添加,但成本極高,有沒有自動化、無侵入的插樁方案呢?以下從iOS、Android、Flutter不同的插樁方案進行簡單的對比。

iOS

iOS中,ObjC首次調用類初始化時,+initialize被執行,系統會自動標記已被調用,在 metaClass的 data的flags字段中的 1<<29 位的這個bit RW_INITIALIZED,就記錄着類是否initialize。可以通過判斷類是否被初始化,因此在運行時,找到合適的時機,遍歷所有類,進行數據的聚合上傳。

Android

Android中,Java語言可以不需要侵入原有代碼,以添加靜態代碼塊的形式添加插樁代碼,buildscript增加編譯插件,在編譯時遍歷所有類文件進行代碼插入即可。

Flutter

Flutter與Android、IOS的方案均有一定差異,Dart沒有Java的靜態代碼塊,也沒有類似ObjC的系統標記。在什麼地方插樁,可以不侵入原有代碼呢?

理論上,Dart Class初始化執行順序爲:

  1. class variables initialize on declaration (no static)

  2. initializer list

  3. superclass’s constructor

  4. main class’s constructor

改寫構造函數會直接侵入原有代碼,Dart構造函數的多樣寫法也增加了自動化插件的難度。因此改寫構造器不是第一選擇。根據初始化執行順序,很快可以想到,是否可以增加新的類成員,初始化時調用插樁代碼,以達到類初始化插樁的效果。例如

但在Dart中,針對擁有常量構建器的類,要求所有的成員均爲final,成員初始化必須在第1第2階段,或構造函數入參進行初始化,即使是extends、with也強制要求子類及Mixin所有的變量均爲final。而Flutter中,Widget等常用組件,均使用常量構建函數,無法通過這種形式插樁。

注入代碼的形式不可用!

還有其他辦法嗎?可不可以通過AOP的方式,hook住所有的類構建器呢?而閒魚技術團隊剛剛開源的AspectD,恰好可以解決這個問題。

AspectD是針對Dart的AOP編程框架,通過Transform實現dill變換以實現AOP,可以便捷地實現無侵入代碼自由注入。

在Flutter v1.12.13下驗證,針對常量構建器、無構建函數、命名爲ClassName.identifier形式構建函數,均測試通過!AspectD代碼如下:

AspectD原理不在此詳細說明,有興趣請移步https://github.com/alibaba-flutter/aspectd。

整體方案設計

FlutterCodeX線上代碼覆蓋率SDK,由編譯時代碼插樁插件、運行時數據採集模塊組成。

  • 代碼插樁插件

編譯時,通過build_runner,CodeXGenerator與CodeAstVisitor進行工程內所有類ast解析,遍歷所有類構造函數,自動生成AspectD的PointCut Execute類文件,hook類構建函數,在構造函數執行完畢後,插樁標記類調用信息,同時還生成項目的完整類列表至構建產物。關鍵代碼如下:

AspectD Execute如下圖所示,類A擁有兩個構造函數,生成兩個AspectD AOP函數。

  • 運行時數據採集模塊

運行時,工程中每個類初始化後將會自動調用addCallTime方法,將類調用信息緩存,選擇用戶退出後臺的時機,進行數據文件進行壓縮上傳,目前我們採用阿里雲OSS文件上傳。根據應用活躍用戶數,設置採樣率,命中至少5萬用戶UV。

  • 數據彙總與產出

最後,線上運行一段時間後,我們將數據彙總,與打包構建產物中的完整類列表進行比對,即可獲得線上代碼覆蓋率數據,推動業務進行行之有效的瘦身。

以簡單Demo工程爲例:

說在最後

目前,FlutterCodeX在閒魚App即將上線,結合客戶端Android、iOS代碼覆蓋率數據,有效地推動廢棄業務下線,助力包體瘦身,對工程健康做長效監控與改善。

閒魚技術團隊不僅是阿里巴巴集團旗下閒置交易社區的創造者,更是移動與高併發大數據應用新技術的引導者與創新者。我們與 Google Flutter/Dart 小組密切合作,爲社區貢獻了多個高 star 的項目和大量 PR 。我們正在積極探索深度學習和視覺技術在互動、交易、社區場景的創新應用。閒魚技術與集團中間件團隊共同打造的 FaaS 平臺每天支持數以千萬級用戶的高併發訪問場景。  

就是現在! 客戶端/服務端java/架構/前端/質量 工程師 面向社會+校園招聘,base杭州阿里巴巴西溪園區,一起做有創想空間的社區產品、做深度頂級的開源項目,一起拓展技術邊界成就極致!

*投餵簡歷給小閒魚→ [email protected]

開源項目、峯會直擊、關鍵洞察、深度解讀

請認準 閒魚技術

相關文章