<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          貨拉拉 Android 動態(tài)資源管理系統(tǒng)原理與實踐(上)

          共 5389字,需瀏覽 11分鐘

           ·

          2022-07-13 18:14

          點擊上方藍字關(guān)注我,知識會給你力量


          ?

          jary,貨拉拉高級客戶端工程師,目前負責貨拉拉App Android端穩(wěn)定性提升,包體積優(yōu)化相關(guān)工作。

          ?
          1. 前言

          • 隨著公司業(yè)務(wù)的擴展,貨拉拉用戶端apk包的體積也不斷變大,過去一年,用戶端android組進行了大量的瘦身工作,取得了較為顯著的成果。再使用常規(guī)方法,已經(jīng)很難優(yōu)化包體積了。
          • 我們可以把一些使用頻率相對較低的資源不打包進apk,并在需要時下載到本地(例如動畫文件,字體,zip壓縮包,so庫等)
          • 我們注意到,貨拉拉用戶端apk中,使用了35個以上的so庫,并且都支持arm64-v8a和armeabi-v7a這2種abi,結(jié)果就是so體積成倍上漲。用戶端生產(chǎn)環(huán)境下的apk,解壓縮后,存放so包的lib目錄,占據(jù)了整個應(yīng)用41%的大小。
          • 因此動態(tài)資源管理系統(tǒng)是下一個優(yōu)化的重點,動畫,字體和zip包只是普通文件,完全可以支持動態(tài)下載并使用。而so文件本質(zhì)上就是一種可動態(tài)加載并執(zhí)行的文件,將 so文件動態(tài)下發(fā)是切實可行的,但是要將它從 apk中剔除并保證穩(wěn)定性并不是一件易事。
          1. 行業(yè)方案

          • 未找到現(xiàn)成的github項目或者三方sdk方案,來實現(xiàn)動態(tài)資源管理。
          • 部分博客提供了動態(tài)管理so文件的思路,但是缺少完整流程。
          • 行業(yè)目前并未提供完整的成熟方案供我們使用,需要我們自己造輪子。
          1. 功能和方案

          • 實現(xiàn)功能

          • 資源分類,預定義了字體,幀動畫,so這3種內(nèi)置資源,以及單個文件,多個文件這2種可自定義資源。
          • 提供通用的加載動態(tài)資源方法,所有資源均可由此加載。
          • 內(nèi)置資源,提供默認的應(yīng)用方法,外部可以直接應(yīng)用。自定義資源,用戶自行決定如何應(yīng)用。
          • 對于所有資源,提供可配置的方便快捷打包方式,減少手動操作。
          • 幾個概念

          • 資源加載:將動態(tài)資源通過下載,校驗,解壓等方式,映射到本地文件的過程。

          該過程對所有資源通用,sdk使用方無需修改資源加載方式。

          • 資源應(yīng)用:動態(tài)資源對應(yīng)的本地文件應(yīng)用到具體業(yè)務(wù)中。例如動態(tài)字體資源的應(yīng)用,就是為TextView設(shè)置一個新的字體。

          該過程每個資源不同,sdk使用方無需修改內(nèi)置資源的應(yīng)用方式,對于自定義資源,需要使用方自行決定應(yīng)用方式。

          • 資源打包:包括生成一個待上傳的資源文件,以及生成資源的Java描述(DynamicPkgInfo類)。so資源還包含了一些方法的hook操作。

          該過程對所有資源都適用,統(tǒng)一使用可配置的dynamic_plugin插件完成。sdk使用方無需修改資源打包方式,但是可通過配置dyanmic_plugin.gradle文件,配置打包過程。

          • 通用資源加載

          • 如何確定資源已經(jīng)下載過了,避免重復下載?

          Java代碼中,使用DynamicPkgInfo類來描述資源,該類中包含了資源的版本號。我們比較該類和本地數(shù)據(jù)庫中的資源版本號,如果不同,才會下載資源。

          • 下載資源是否提供多線程下載,斷點續(xù)傳等功能?

          本sdk只提供了下載接口,未提供實際下載功能,因此如需這些功能,需要調(diào)用者自己實現(xiàn)。

          • 如何校驗資源,防止被篡改?

          DynamicPkgInfo類中包含了資源校驗信息,我們利用該類,對下載好的文件進行md5碼,文件長度,文件名稱的校驗。

          • 如何判斷資源是否壓縮包,以及如何解壓縮?

          目前簡單的采用后綴名是否為.zip判斷,使用使用Java內(nèi)置java.util.zip包下工具解壓。

          • 如何校驗解壓后的資源子文件,防止被篡改?

          DynamicPkgInfo同樣包含了zip包中所有子文件的校驗信息,我們利用它,來校驗所有解壓后的文件。

          資源應(yīng)用

          • 字體資源應(yīng)用,從加載好的本地文件中,創(chuàng)建系統(tǒng)Typeface字體對象,并設(shè)置到TextView上。
          • 幀動畫資源應(yīng)用,從加載好的本地文件中,創(chuàng)建系統(tǒng)AnimationDrawable幀動畫對象,并設(shè)置到ImageView上。
          • 字體和幀動畫資源的應(yīng)用流程,見第5章,內(nèi)置資源應(yīng)用流程。
          • so資源應(yīng)用流程,見第7章,so資源加載和應(yīng)用解決方案。
          • 自定義資源的應(yīng)用,需要sdk使用者自己定義。
          • 資源打包

          我們使用dynamic_plugin gradle插件來完成所有資源的打包。

          • 字體資源打包

          • 掃描輸入目錄字體文件,將他們拷貝到輸出目錄。
          • 為每個字體生成一個DynamicPkgInfo類的常量,代表該動態(tài)資源。
          • 幀動畫資源打包

          • 掃描輸入目錄幀動畫文件夾,將它們逐個壓縮,并將壓縮包輸出到指定目錄。
          • 為每一組幀動畫生成一個DynamicPkgInfo類的常量,代表該動態(tài)資源。
          • so資源打包

          • Hook系統(tǒng)System.loadLibrary方法的調(diào)用。
          • 系統(tǒng)打包流程中,刪除配置文件指定so文件,并將他們拷貝到指定目錄。
          • 掃描上面的so文件目錄,將他們逐個壓縮,并將壓縮包輸出到指定目錄。
          • 為每一個so壓縮包生產(chǎn)一個DynamicPkgInfo類的常量,代表該動態(tài)資源。
          • 自定義資源打包

          • 單個文件的資源打包同字體資源
          • 多個文件的資源打包同幀動畫資源
          • 運行產(chǎn)物

          • 下圖為該打包插件運行一次之后的產(chǎn)物。
          • input目錄,所有待打包資源的存放目錄,我們需要手動把要打包的資源拷貝這里,例如字體文件拷貝到input/typeface目錄下。注意so資源會在打包過程中,自動生成,無需手動處理。
          • output目錄,則是打包出來的產(chǎn)物,包括字體資源,so資源,幀動畫資源等,我們可以手動將此目錄下的打包后資源上傳到服務(wù)器。
          • DynamicResConst.java文件,該文件中生成了所有資源的信息。
          • DynamicResConst.java文件的內(nèi)容,我們在這里也稍微看一下,圖中為字體資源和幀動畫資源的java描述。可以看到所有動態(tài)資源,都用DynamicPkgInfo類來描述。
          • 單個文件資源,包含了資源的id,文件名稱,資源類型,下載地址,版本號,文件長度以及md5碼。
          • 多個文件資源,除了包含上述信息外。還包含了該壓縮包解壓后,里面每個文件的名稱,文件長度以及md5碼
          1. 整體架構(gòu)

          由于整個系統(tǒng)功能較復雜,我們將其分為3個module。

          • lib_dynamic_base:只包含md5,壓縮解壓等通用操作以及代表資源的實體類DynamicPkgInfo,該module為后面2個module的基礎(chǔ)。
          • lib_dynamic_res:提供了資源的加載和應(yīng)用功能,目前包含字體資源,幀動畫資源,so資源以及自定義資源。
          • dynamic_plugin:為一個gradle plugin工程,提供了資源打包功能。
          • lib_dynamic_res模塊架構(gòu)

          該庫包括了動態(tài)資源加載和應(yīng)用全過程,我們分為5層實現(xiàn)

          • 外部接口層,主要為加載管理器和加載監(jiān)聽器,提供了所有外部的接口。
          • 資源應(yīng)用層,封裝了幾種內(nèi)置動態(tài)資源的應(yīng)用,字體資源,幀動畫資源,so資源。
          • 加載流程層,具體完成了資源的加載過程,主要采用狀態(tài)模式實現(xiàn),包括一個狀態(tài)管理器,以及各種狀態(tài),例如檢查本地版本狀態(tài),下載狀態(tài),校驗文件狀態(tài)等。
          • 接口隔離層,主要是一些功能接口,例如下載功能,解壓縮功能,上報功能等,隔離了底層實現(xiàn)。
          • 具體實現(xiàn)層,各個具體功能的實現(xiàn),例如數(shù)據(jù)庫操作,java zip庫等。
          • dynamic_plugin插件架構(gòu)

          • 系統(tǒng)插件層,主要為系統(tǒng)gradle plugin的實現(xiàn),以及對dynamic_plugin.gradle配置文件的讀取和解析
          • 任務(wù)模塊層,包含了各個任務(wù),例如刪除并拷貝so文件任務(wù),壓縮zip包任務(wù)等。
          • 底層實現(xiàn)層,包含了具體功能的實現(xiàn),例如asm框架和transform api,zip壓縮,javepoet代碼生成等。
          1. 通用資源加載,內(nèi)置資源應(yīng)用流程

          • 通用資源加載主流程

          加載普通資源的主流程如下,首先判斷資源包指定版本號和本地數(shù)據(jù)庫版本號是否相同,如果想同,進入本地資源校驗流程,否則進入下載流程。

          • 下載校驗解壓流程

            • 我們首先調(diào)用下載接口下載資源。
            • 如果下載成功,我們校驗下載文件,下載失敗,則嘗試刪除文件,并直接跳到失敗結(jié)果。
            • 校驗下載文件成功,我們在判斷是否為zip文件,對于zip文件,我們執(zhí)行解壓縮操作,非zip文件,直接成功。
            • 解壓縮完成后,我們在對解壓后的所有文件執(zhí)行校驗操作。
          • 本地資源校驗流程

            • 對于下載并解壓的壓縮包資源,以及本地數(shù)據(jù)庫版本和資源實體類版本號相同的資源,我們需要進行本地資源校驗流程。
            • 遍歷資源包指定的字文件列表,對他們進行逐個文件檢驗就可以了。
          • 單個文件校驗流程

          資源實體類中指定的文件名稱,文件長度,文件md5碼和本地文件相同時,我們認為該文件校驗成功了

          • 加載恢復流程

          動態(tài)資源加載過程中,可能因為各種原因,導致加載未能得到成功或者失敗的結(jié)果,而在中間狀態(tài)被中斷,如應(yīng)用進程被殺死,手機關(guān)機等等。為了避免加載意外中斷的情況下,完全從頭開始進行加載,我們設(shè)計了一個動態(tài)資源加載的恢復流程,如果異常中斷,我們下次加載資源時,可以恢復到當前狀態(tài),繼續(xù)進行加載。

          • 下載過程的恢復和斷點續(xù)傳,需要下載接口的實現(xiàn)者負責。
          • 其他狀態(tài),我們在狀態(tài)改變時,將資源id,當前狀態(tài)和待處理文件路徑,保存到數(shù)據(jù)庫。
          • 每次加載動態(tài)開始時,根據(jù)資源id查找數(shù)據(jù)庫中是否有待恢復數(shù)據(jù)。
          • 有待恢復數(shù)據(jù),轉(zhuǎn)到待恢復的狀態(tài),否則,直接去檢查版本號狀態(tài)。
          • 資源加載成功或者失敗時,從數(shù)據(jù)庫中刪除當前資源id對應(yīng)的恢復狀態(tài)。
          • 內(nèi)置資源應(yīng)用流程

          前面我們總結(jié)了動態(tài)資源的加載流程,資源加載完成后,我們還需要將該資源進行應(yīng)用,而這里我們要說的就是將動態(tài)資源應(yīng)用到對應(yīng)View上的流程。

          • 根據(jù)資源id,從緩存中獲取動態(tài)資源對應(yīng)的本地文件。
          • 文件獲取成功,直接設(shè)置到view上,獲取失敗,進入下一步。
          • 參數(shù)列表中的占位資源不為空,則將占位資源設(shè)置到View上。
          • 將資源id設(shè)置到View的tag上,嘗試清除上次動態(tài)資源加載失敗狀態(tài)。
          • 使用管理器Manager類的load方法,執(zhí)行之前的加載流程。
          • 異步等待加載完成回調(diào),判斷資源id是否和View的tag相同,防止view被復用,導致的資源錯亂情況。
          • 如果Activity沒有被銷毀,則將資源設(shè)置到View上。
          1. lib_dynamic_res模塊類設(shè)計

          可與第4章,整體架構(gòu)分層圖對照著看

          • 外部接口層

          DynamicResManager類負責和外部交互,提供了初始化(init),加載資源(load),isResReady(判斷資源是否就緒),clearFailState(清除錯誤狀態(tài)等方法)等方法。

          Config類,則可以向管理器提供線程池,下載器接口,本地資源信息接口,本地資源狀態(tài)接口等配置信息。

          AbsResInfo抽象類,代表動態(tài)資源。

          DynamicPkgInfo類,AbsResInfo的子類,提供給外部使用,代表了一個動態(tài)資源實體。

          DynamicPkgInfo.FileInfo,AbsResInfo的子類,資源實體內(nèi)部類,代表了資源中的一個子文件。

          DynamicPkgInfo.FolderInfo,AbsResInfo的子類,資源實體內(nèi)部類,代表了資源中的一個子文件夾。

          ILoadResListener接口,提供了加載資源時的回調(diào)功能,會回調(diào)加載成功,失敗,狀態(tài)變化,下載中進度

          • 資源應(yīng)用層

          AbsResApply抽象類,實現(xiàn)了動態(tài)資源在ui元素上的應(yīng)用。

          TypefaceResApply類,AbsResApply的子類,代表了字體資源的應(yīng)用。

          FrameAnimApply類,AbsResApply的子類,代表了幀動畫資源的應(yīng)用。

          AbsSoLoad抽象類,實現(xiàn)了so動態(tài)資源的應(yīng)用。

          RelinkerSoLoad類,AbsSoLoad的子類,使用Relinker第三方庫最終load so庫。

          SystemSoLoad類,AbsSoLoad的子類,使用系統(tǒng)System.loadLibrary方法最終load so庫

          • 加載流程層

          我們使用狀態(tài)模式來控制整個動態(tài)資源的加載流程。

          IState,狀態(tài)接口,代表了加載流程中的一個狀態(tài)。

          InitState類,初始化狀態(tài)。

          CheckVersionState類,檢查資源實體類版本號與數(shù)據(jù)庫版本號是否相同狀態(tài)。

          DownloadState類,下載資源狀態(tài)。

          VerrifyFileState,校驗下載資源狀態(tài)。

          UnZipState,解壓縮下載資源狀態(tài)。

          VerifyZipState,校驗解壓后的所有文件狀態(tài)。

          IStateMechine,狀態(tài)管理機接口,負責管理前面所有的IState對象。

          DefaultStateMachine類,狀態(tài)管理機的默認實現(xiàn)。

          ResCtx類,狀態(tài)管理機運行過程中的全局context對象,存儲了路徑信息,加載成功信息,加載失敗異常等全局信息。

          • 接口隔離和具體實現(xiàn)層

          這2層的類,較為雜亂,限于篇幅,我們就不一一列舉了。

          • 類uml圖

          ……未完待續(xù)……


          向大家推薦下我的網(wǎng)站 https://xuyisheng.top/  點擊原文一鍵直達

          專注 Android-Kotlin-Flutter 歡迎大家訪問



          往期推薦


          本文原創(chuàng)公眾號:群英傳,授權(quán)轉(zhuǎn)載請聯(lián)系微信(Tomcat_xu),授權(quán)后,請在原創(chuàng)發(fā)表24小時后轉(zhuǎn)載。
          < END >
          作者:徐宜生

          更文不易,點個“三連”支持一下??


          瀏覽 74
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  操B视频在线观看 | 久久久亚洲AV无码日韩精品 | 亚洲www啪成人一区二区麻豆 | 天天日,天天干,天天射 | 操逼网站在线免费观看 |