<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                [TOC] # AAB ## App Bundle 文件格式 Android App Bundle是一種全新的應用上傳格式(.aab),它包含所有編譯代碼和資源。當您上傳aab文件至Google Play后,Google Play將aab文件拆分成一系列APKs并簽名。 ![](https://img.kancloud.cn/af/f7/aff73d9c348cd6814265d8bd5c477b21_992x650.png) ![](https://img.kancloud.cn/85/5c/855c698a584b420b0f4cc1a23e6f9412_1200x582.png) ### AAB 中的內容和 APK異同 | aab fiels | descriptions | | :-- | :-- | | base/feature1/feature2 | base 是應用的基本功能,feature 承載各 DynamicFeature 的內容(后文介紹) | | manifest.xml | APK 中只有一個 manifest 且是二進制格式,AAB 會存在于每個模塊中e中,且使用 ProtoBuf(pb)格式,便于處理 | | dex | 與 APK 不同,AAB 將每個模塊的 dex 文件存儲在各自目錄中 | | res/assets/libs | 該目錄與 APK 中相同,當上傳 AAB 時,GP 會檢查這些目錄并僅打包滿足目標設備需要的最小文件 | | resources.pb | 類似于 resource.arsc 文件,是一個資源索引表,其中描述了應用程序內部存在的資源和目標的細節,可用于 GP 針對不同設備配置 APK。 | | assets.pb | 相當于應用程序 assets 的資源表,可用于 GP 針對不同設備配置 APK。例如將 assets 資源放到 assets/languages#lang\_xx 或 assets/i18n#lang\_xx 路徑下,則會根據語言配置下發 assets 資源。 | | native.pb | ?這相當于native庫的資源表,可用于 GP 針對不同設備配置 APK | 后三個`.bp`文件是 AAB 格式的重要部分,它們描述了 APP 的不同服務目標,動態下發根據這些目標從 `drawable/hdpi`、`lib/armeabi-v7a` 或者 `values/es` 等路徑中組織不同資源進行下發。 ## Dynamic Feature 此外,您也可以在應用項目中添加dynamic feature模塊,這些模塊并不需要在應用首次安裝時一起被下載安裝。您可以通過使用Play Core Libray在應用運行過程中動態安裝dynamic feature。dynamic feature類似國內插件化提供的能力,但dynamic feature功能更強大。 通過下圖,可以看到dynamic feature可以基于設備配置選取對應的Configuration Split APKs,如此可以進一步減小dynamic feature安裝包體積。 ![](https://img.kancloud.cn/5b/14/5b14a12918e6305a92e9cb9eaecba6d9_1200x828.png) ### Split APKs(Android5.0) Android App Bundle之所以能夠支持應用運行期間安裝dynamic feature,得益于Android 5.0推出的Split APKs功能。 Split APKs是Android 5.0引入的一種全新應用安裝機制,其目的是為解決APK體積日益增大問題。Split APK可以將一個完整龐大的APK按照CPU架構、屏幕密度等維度拆分成多個獨立APKs。當應用APK下載更新時,依據當前設備配置選取對應配置APKs安裝即可。 Android 5.0之前,一個APK代表一個應用。在Split APKs問世之后,一個應用可能對應多個APKs。所有Split APKs擁有相同包名和簽名。 Android提供兩種方式安裝Split APKs。 adb install-multiple [base-apk, split1-apk] PackageInstaller. Android App Bundle為dynamic feature提供全新插件com.android.dynamic-feature,它的編譯產物是.apk文件。當您的項目編譯完成后,Android Studio通過命令adb install-multiple命令將base apk和split apks安裝至您的手機。如果您的開發手機系統版本低于5.0,則會依據當前手機設備組裝成一個完整apk文件安裝至該手機。 ![](https://img.kancloud.cn/c9/70/c970587017cce5654631afdcb404b0b7_1200x741.png) 下面我們重點介紹第二種安裝方式,Android 5.0提供PackageInstaller用于安裝Base APK和Split APKs。 #### PackageInstaller 當第三方應用通過PackageInstaller在應用運行期安裝Split APKs時,系統會啟動安裝器界面供用戶選擇是否安裝此次更新。 ![](https://img.kancloud.cn/fe/e7/fee74cd2d081eadf72efa4e622f5e5e9_1200x814.png) 在用戶選擇安裝后,應用將會被系統“殺死”。當應用再次啟動之后,Split APKs就會生效。 在我們實際測試過程中,某些國產手機對PackageInstaller有改動,導致無法正常安裝Split APKs。 系統應用可以靜默安裝Split APKs,且當Split APKs安裝完成后,可以決定是否“殺死“應用進程。 ``` public static class SessionParams implements Parcelable { /** {@hide} */ @SystemApi public void setDontKillApp(boolean dontKillApp) { if (dontKillApp) { installFlags |= PackageManager.INSTALL_DONT_KILL_APP; } else { installFlags &= ~PackageManager.INSTALL_DONT_KILL_APP; } } ... ... } ``` SessionParams是PackageInstaller內部類,setDontKillApp可決定當APK安裝完成后是否殺死應用進程。setDontKillApp屬于系統Api,因此第三方應用無法調用。 ### Split APKs加載原理 ![](https://img.kancloud.cn/3f/e5/3fe50bd73d204873270c613db51e69b2_1200x705.png) 通過Android 9.0 LoadedAPK源碼片段,我們一起了解下Split APKs加載過程。 是的,系統級的代碼本身就支持了Split Apk的加載 #### ClassLoader 通過createOrUpdateClassLoaderLocked方法名,可以知道該方法是用于創建和更新ClassLoader ![](https://img.kancloud.cn/87/23/8723ba9a1a73db1b8d0291e14644f958_1200x808.png) 該方法有兩個核心步驟。 如果mClassLoader為空,則創建PathClassLoader實例。 如果addedPaths不為空,則更新PathClassLoader實例 該方法指明,應用進程是可以動態加載Split APKs代碼 #### Resources 通過getResources方法代碼片段,可知Split APKs的資源路徑作為mResources創建參數 ![](https://img.kancloud.cn/6a/9a/6a9a07ea8b18a4ff4ccb56c363b97de1_1200x694.png) #### 不支持四大組件的新增 Android App Bundle在Manifest文件合并過程中,會將split APKs manifest文件內容合并至base APK中。因此,所有split APKs四大組件信息都是已經聲明在base APK中。 Android App Bundle這種處理方式不支持Manifest更新,例如新增四大組件 #### 多進程問題 Android App Bundle所支持的功能特性有部分局限性 多進程問題 依據Qigsaw安裝、加載split APKs原則,當游戲APK安裝完成后,就會在主進程完成加載。在游戲APK中有兩個Activity,他們所處進程不同。當啟動GameActivity01時,頁面正常啟動。但當啟動GameActivity02,您的App會出現崩潰。原因是GameActivity02運行在:game進程,游戲APK僅在主進程加載,并未在:game進程加載,因此系統會拋出ClassNotFoundException異常。 為解決這類問題,Qigsaw提供了如下解決方案。 在進程啟動之初即Applicatin#attachBaseContext調用時,加載所有已安裝splits。 第一種方案解決的場景是:game進程首次啟動,即啟動GameActivity02之前:game進程從未啟動過。 Hook PathClassLoader。 第二種方案解決的場景是:game進程已經啟動并正在運行 Hook PathClassLoader具體做了如下事情。 當出現ClassNotFoundException時,判斷該類是否為splits四大組件。 當異常類為splits四大組件時,加載所有已安裝未加載split APKs。 如加載完所有已安裝未加載split APKs后依然出現ClassNotFoundException異常,則返回空四大組件類,防止進程崩潰。 # 愛奇藝動態化框架Qigsaw ## 背景知識 在2018年上半年,我們就進行動態組件化方案的調研。起初方案是基于Instant App方案實現,當整體功能基本實現后,Google于2018年Google IO大會上推出Android App Bundle。在調研Android App Bundle之后,我們發現Android App Bundle完全符合最初的需求。 依據我們最初設計初衷和Android App Bundle特點,總結出Qigsaw應滿足以下核心特點。 利用Android App Bundle開發套件,體驗原生極速開發體驗。 少量私有Api訪問,保證框架穩定性。 如果您的應用有出海需求,可無縫切換至Android App Bundle方案。 關于私有Api訪問應該是大家比較關心的,最近一段時間某大廠開源了號稱零反射插件化框架,但是通過閱讀其源碼,我們發現它還是做了PathClassLoader的parent ClassLoader反射替換。 另外它也調用了Resources構造方法創建Resources實例,雖然這樣做并沒有任何私有Api訪問,但是通過查看Resources構造方法源碼,我們可知該方法屬于過時方法,且注釋寫明第三方應用不應該創建Resources實例。 所以插件化框架不應該僅僅以是否零反射為目標,我們應該從開發流程及產品形態選取合適方案,助力開發效率。 ## 比googlePlay更好的打包體驗 在發布階段,Qigsaw提供打包插件讓開發者享受一條龍服務,開發者不必關心dynamic feature的上傳分發。 Qigsaw打包插件支持內置dynamic feature,所有內置dynamic feature都會被拷貝至base apk的assets目錄。對于非內置dynamic feature,Qigsaw打包插件會將其上傳至CDN服務器,解決業務方后顧之憂。 ## Qigsaw原理還是基于插件化 Qigsaw借助Android App Bundle開發套件完成dynamic feature的打包,大大降低Qigsaw開發維護成本。因此Qigsaw關心的重點落在 如果安裝加載dynamic feature生成apk上。 第三方應用利用PackageInstaller安裝split APKs體驗極其不友好,且某些國產手機對split APKs功能支持不完善,所以我們最終還是按照一般插件化方式安裝加載split APKs。 ![](https://img.kancloud.cn/2b/f7/2bf75d76d4903021c2d94389483526af_1200x717.png) 依據上圖,如果需要動態加載split APKs,需要解決代碼、資源以及四大組件的加載 ### 代碼加載使用打補丁 針對splits代碼加載,Qigsaw采用單類加載器方式,即base APK和split APKs采用同一ClassLoader加載。 ![](https://img.kancloud.cn/78/bd/78bdb2adbee85d3001790c1f0c5ae9b9_1200x724.png) 在DexPathList中,為每個split創建對應的Element和NativeLibraryElement實例即可。關于單類加載器更多細節,本文不再贅述,相關原理已非常成熟。 ### 資源加載基于打補丁的方式同時不會沖突 ![](https://img.kancloud.cn/5d/b4/5db4d9d08ba781d7f00cbd7a8793401f_1200x667.png) Splits資源加載相較于代碼加載會復雜,因為不同系統版本或不同手機廠商都會存在一些兼容性問題。 資源id沖突問題 Android Gradle Plugin在資源打包時,會對res目錄下資源文件分配一個唯一Id。 * Id前兩位PP為Package Id,代表應用類型。是系統應用、第三方應用、Instant App或Dynamic Feature等。 * Id中間兩位TT為Type,代表資源類型。是drawable、layout或string等。 * Id后四位EE為Entry,代表該資源順序。 所有第三方應用base APK資源Package Id均為7F,Android App Bundle對splits資源打包時會基于7F依次遞減分配Package Id。因此,即使我們將split APKs資源添加到當前應用Resources實例中,也不會出現資源沖突問題,splits訪問base資源也更加方便<.font> Instant Apps資源打包是基于7F依次遞增。 **反射Resource對象** ![](https://img.kancloud.cn/21/f5/21f55cfef3db04348bd6cb25d9192eb1_1200x740.png) Qigsaw提供loadResources方法加載split APKs資源。為避免開發者寫大量模板代碼,Qigsaw打包插件采用字節碼操作方式自動寫入該方法。 ### 不支持四大組件的新增 Android App Bundle在Manifest文件合并過程中,會將split APKs manifest文件內容合并至base APK中。因此,所有split APKs四大組件信息都是已經聲明在base APK中。 Android App Bundle這種處理方式不支持Manifest更新,例如新增四大組件,所以Qigsaw也不支持新增四大組件。在正常開發迭代過程中,動態新增splits四大組件需求極少,所以Qigsaw與Android App Bundle特性保持一致。 # 參考資料 [AAB 扶正!APK 再見!](https://juejin.cn/post/6984588418554527774) [# Android App Bundle動態化方案](https://blog.csdn.net/fei20121106/article/details/108034651)
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看