bazel 行動安裝

回報問題 查看來源

快速進行 Android 的疊代開發

本頁說明 bazel mobile-install 如何大幅加快 Android 的疊代開發作業。以下說明這個方法相較於傳統應用程式安裝方法的難題的優點。

摘要

如要快速安裝對 Android 應用程式的小變更,請按照下列步驟操作:

  1. 找到要安裝的應用程式,找出 android_binary 規則。
  2. 移除 proguard_specs 屬性即可停用 Proguard。
  3. multidex 屬性設為 native
  4. dex_shards 屬性設為 10
  5. 透過 USB 連接執行 ART (而非 Dalvik) 的裝置,然後啟用 USB 偵錯功能。
  6. 執行 bazel mobile-install :your_target。 應用程式啟動速度會比平常慢。
  7. 編輯程式碼或 Android 資源。
  8. 執行 bazel mobile-install --incremental :your_target
  9. 不用再等太多了,

以下為 Bazel 的指令列選項,或許能派上用場:

  • --adb 會告知 Bazel 要使用哪個 ADB 二進位檔
  • --adb_arg 可用於在 adb 的指令列中加入額外的引數。如果您有多部裝置連線至工作站,請選取要安裝的裝置:bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
  • --start_app」會自動啟動應用程式

如有疑問,請查看範例與我們聯絡

簡介

開發人員工具鍊中最重要的屬性之一,就是速度:變更程式碼後,在一秒內就能執行程式碼,但要等幾分鐘,有時需要等數小時,才能收到任何意見回饋,讓您瞭解變更是否符合預期。

遺憾的是,建構 .apk 的傳統 Android 工具鍊包含許多用於建構 Android 應用程式的單體式依序步驟,且必須完成所有這些步驟才能建構 Android 應用程式。在 Google 中,如果等待五分鐘的時間建立單行變更,對 Google 地圖這類大型專案而言是不尋常的。

bazel mobile-install 結合變更裁切、工作資料分割和巧妙操控 Android 內部,讓 Android 的疊代開發速度更快,而且完全不必變更應用程式的程式碼。

傳統應用程式安裝相關問題

建構 Android 應用程式有一些問題,包括:

  • Dex 處理。根據預設,dx 在建構作業中只會叫用一次,而且不知道如何重複使用先前建構中的工作:即使只有變更一個方法,也會重新執行所有方法。

  • 將資料上傳至裝置。ADB 不會使用 USB 2.0 連線的全部頻寬,而較大的應用程式可能需要相當長的上傳時間。即使只有小部分 (例如資源或單一方法) 變更,也會上傳整個應用程式,因此這可能會成為主要的瓶頸。

  • 編譯至原生程式碼。Android L 推出了新的 Android 執行階段 ART,這種新的 Android 執行階段能夠預先編譯應用程式,而不是像 Dalvik 一樣及時編譯。這樣可以加快應用程式的速度,但安裝時間較長。這是對使用者而言有利的取捨,因為使用者通常只安裝應用程式一次,且使用多次,但會導致開發速度變慢,因為使用者安裝應用程式數次,每個版本的執行次數最多。

bazel mobile-install 的做法

bazel mobile-install進行下列改善:

  • 資料分割 dex 處理。Bazel 會在建構應用程式的 Java 程式碼後,將類別檔案分割成大約大小相同的部分,並對這些檔案個別叫用 dx。系統不會在上次建構後未變更的資料分割上叫用 dx

  • 漸進式檔案傳輸。Android 資源、.dex 檔案和原生程式庫會從主要 .apk 中移除,並儲存在獨立的行動裝置安裝目錄下。如此一來,您不必重新安裝整個應用程式,即可獨立更新程式碼和 Android 資源。因此,傳輸檔案所需的時間較短,而且只有變更過的 .dex 檔案會在裝置端重新編譯。

  • 從 .apk 外部載入應用程式的部分內容。一個小型的虛設常式應用程式位於 .apk 中,這個檔案會從裝置端的行動裝置安裝目錄載入 Android 資源、Java 程式碼和原生程式碼,然後將控制權轉移到實際應用程式。這會對應用程式公開,但以下幾個極端情況除外。

資料分割 Dex 處理

資料分割的 dex 相當簡單明瞭:建立 .jar 檔案後,工具會將這些檔案分割成大小相等大小相同的個別 .jar 檔案,然後針對自上次建構後變更的 .jar 檔案叫用 dx。決定哪些資料分割要 dex 並非 Android 專用,而是使用 Bazel 的一般變更修剪演算法。

資料分割演算法的第一個版本只會按照字母順序排序 .class 檔案,然後將清單切割為大小相等的部分,但成效不佳:如果新增或移除類別 (即使是巢狀或匿名類別),則所有類別在開始位移後都會按字母順序排列,導致所有類別再次解開這些資料分割。因此,他們決定分割 Java 套件,而非個別類別。當然,如果新增或移除新套件,這仍會導致多個資料分割,但與新增或移除單一類別的頻率大幅減少。

資料分割數量是由 BUILD 檔案 (使用 android_binary.dex_shards 屬性) 控制。在理想情況下,Bazel 會自動判斷數量最佳的資料分割數量,但 Bazel 目前必須在執行任何動作前知道一組動作 (例如要在建構期間執行的指令),因此無法判斷分割作業的最佳數量,因為啟動程序越慢,應用程式最終會進行更多的 Java 類別建構速度就比較慢。理想的位置通常介於 10 至 50 個資料分割之間。

檔案傳輸增量

應用程式建構完畢後,下一步最好以最省時的方式進行安裝。安裝程序包含下列步驟:

  1. 安裝 .apk (通常使用 adb install)
  2. 將 .dex 檔案、Android 資源和原生資料庫上傳至行動裝置安裝目錄

第一個步驟的成效不大,也就是說應用程式已安裝或尚未安裝。Bazel 目前取決於使用者是否應透過 --incremental 指令列選項執行這個步驟,因為在所有情況下都無法判斷。

在第二個步驟中,系統會將建構中的應用程式檔案與裝置端資訊清單檔案進行比較,該檔案會列出裝置上有哪些應用程式檔案及其檢查碼。系統會將任何新檔案上傳到裝置、更新的所有檔案,以及從裝置中移除任何已移除的檔案。如果資訊清單沒有資訊清單,系統會假設每個檔案都必須上傳。

請注意,您可以透過在裝置上變更檔案 (而非資訊清單中的總和檢查碼),藉此欺騙漸進式安裝演算法。這個功能可以計算裝置上的檔案總和檢查碼,藉此防範上述問題,但我們認為這種情況下的安裝時間不會增加。

Stub 應用程式

虛設常式應用程式是實用的功能,可從裝置端的 mobile-install 目錄載入 Dexe、原生程式碼和 Android 資源。

實際載入是將 BaseDexClassLoader 設為子類別,可以採取合理記錄技術的做法。這個情形會在應用程式的任何類別載入前進行,因此,APK 中的所有應用程式類別都可以放在裝置的 mobile-install 目錄中,以便在沒有 adb install 的情況下更新。

這項作業必須在應用程式的任何類別載入前進行,因此 .apk 中不需要任何應用程式類別,也就是說,變更這些類別時便需要完整重新安裝。

方法是將 AndroidManifest.xml 中指定的 Application 類別替換為虛設常式應用程式。這樣可控制應用程式的啟動時間,並在最早期的階段 (其建構函式) 使用 Android 架構內部的 Java 反射來微調類別載入器和資源管理員。

虛設常式應用程式還有一項功能,就是將行動裝置安裝時安裝的原生資料庫複製到其他位置。此為必要步驟,因為在檔案上設定動態連接器需要 X 位元,這對於非根 adb 存取的任何位置都無法執行此操作。

完成上述所有操作後,虛設常式應用程式會將實際的 Application 類別例項化,變更 Android 架構中實際應用程式的所有參照。

結果

效能

一般來說,只要稍做調整,bazel mobile-install 就能加快建構和安裝大型應用程式的 4 至 10 倍速度。

我們針對部分 Google 產品計算了下列數字:

當然,這也取決於變更的性質:變更基礎程式庫後重新編譯,將需要較多時間。

限制

並非所有情況都能使用虛設常式應用程式。以下列舉幾個功能無法正常運作的情況:

  • Context 轉換為 ContentProvider#onCreate() 中的 Application 類別時。在應用程式啟動期間,系統會在有機會替換 Application 類別的執行個體之前呼叫此方法,因此 ContentProvider 仍會參照虛設常式應用程式,而非真正的虛設常式應用程式。沒錯,這並非錯誤,因為您沒有像這樣調降 Context 的問題,但這似乎是在 Google 的幾個應用程式中發生。

  • bazel mobile-install 安裝的資源只能在應用程式內使用。如果其他應用程式透過 PackageManager#getApplicationResources() 存取資源,這些資源就會來自上次非漸進式安裝。

  • 並非執行 ART 的裝置。雖然虛設常式應用程式可以在 Froyo 和以上版本中順利運作,但 Dalvik 有一項錯誤,導致如果應用程式的程式碼在特定情況下 (例如透過特定方式使用 Java 註解) 發布至多個 .dex 檔案,就會認為應用程式不正確。只要應用程式不會引起這些錯誤,就應該能與 Dalvik 搭配使用 (請注意,支援舊版 Android 並非我們重視的重點)