精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Android 插件化中資源錯亂的解決方案

精選
移動開發 Android
本文介紹了 Android 插件化框架中,插件使用宿主資源時資源錯亂的問題,以及錯亂的原因、業界通用解決方案、我們提出的優化方案。

摘要

本文介紹了 Android 插件化框架中,插件使用宿主資源時資源錯亂的問題,以及錯亂的原因、業界通用解決方案、我們提出的優化方案。

本文將按照如下順序,循序漸進地進行講解:

  • 簡單介紹 Android 插件化中資源部分的動態化。
  • 簡單介紹 Android 中的資源的一些基礎知識、使用方式及其編譯原理。
  • 介紹插件化場景下出現的資源錯亂問題及業界通用的解決方案。
  • 介紹一種新的方案——免資源固定方案,用于解決資源錯亂問題。
  • 單獨介紹一下免資源固定方案中的一個技術點:修改 apk 中的資源文件。

1. Android 插件化中資源的動態化

Android 發展了這么多年,市面上涌現出許多插件化/熱修復框架,無論是插件化還是熱修復,都是為了實現對主apk以外內容的動態化,這些內容包括 dex(class)、res(資源)、so(動態庫)等。對于每一種內容,業界都有許多實現方案,盡管方案各不相同,但底層原理都差不多,網上也有許多文章和開源項目可以學習參考。

名詞解釋

宿主:直接安裝到用戶手機上的 App,宿主中的代碼在宿主安裝到用戶手機上的那一刻就定死了,不能再改變了(熱修復也只是讓錯誤的邏輯不走而已,并沒有改變原有的代碼)。

插件:獨立于宿主之外的一個文件。需要被宿主動態加載的 class、res、so 等的集合。(熱修復中這部分通常稱為 patch,這里為了方便,就叫插件吧)

java 代碼:為了描述方便,apk 中的 dex 在編譯前一律稱為 java 代碼,編譯后一律稱為 dex(這個說法不準確,不要被我誤導了,一般為java / kotlin- > class- > dex )

說到 Android 資源的動態化,思路都大同小異:

  • 為每個插件創建一個 Resources 或者把插件的資源路徑添加到宿主 AssetManager,從而可以順利的加載到插件資源。
  • 插件編譯時通過配置 aapt2 參數對插件中資源 id 的 packageId 部分進行修改,保證插件與宿主資源 id 不沖突。
  • 對于插件中使用到的宿主資源,利用 aapt2 參數進行資源固定,保證宿主升級后插件使用到的宿主資源 id 不變。

aapt2 的出現使資源固定、packageId 修改變得容易了很多!

盡管 Android 資源的動態化技術已經十分成熟,但是在實踐過程中還是有許多不足,比如“資源固定”就經常被業務同學吐槽。

2. Android 中的資源介紹

在介紹資源固定之前,首先簡單介紹一下 Android 中資源相關的基礎知識。

2.1  Android 中的資源 id

Android 代碼在編譯成 apk 之后,每個資源都對應一個唯一的資源 id,資源 id 是一個 8 位的 16 進制 int 值 0xPPTTEEEE :

  • PP :前兩位是 PackageId 字段,系統資源是 01,宿主資源 id 是 7f,其他如廠商自定義的皮膚包、webview 插件資源包會占用 02、03......,因此 App 資源和系統資源永遠不會沖突。市面上的插件框架為了保證插件和宿主資源不沖突,通常會把插件資源的 PP 改為其他值,如 7e、7d。
  • TT :中間兩位是 TypeId 字段,表示資源的類型,如 anim、drawable、string 等,這塊沒有嚴格的對應關系,通常是按照字母順序分配 type 值。
  • EEEE :最后四位是 EntryId 字段,用于區分同一個 PackageId、同一個 TypeId 下不同 name 的資源,通常也是按照字母順序進行分配的。

注意:

  • 資源 id 的分配默認是按資源的字母排序進行的,也就是說,當新增一個 name 為 a 的資源,重新編譯之后,a 后面的同類型的資源 id 值都會被改變。
  • aapt2 中提供了參數可以對資源 id 分配方式進行干預,aapt2 會優先按照參數中配置的對應關系分配 id,這個技術我們稱之為資源固定,也是目前插件化框架在解決資源錯亂問題中用的最多的技術。

2.2  Android 中的資源使用方式

Android 中使用資源通常有兩種方式:

  1. 在 java 代碼中通過 R 的內部類進行訪問,具體語法為:
[<package_name>].R.<resource_type>.<resource_name>
  1. 在 xml 中通過符號使用,具體語法為:
@[<package_name>:]<resource_type>/<resource_name>

xml 中也可以通過 ? 代替 @ 的形式引用樣式屬性。也可以引入自定義屬性,如 android:layout_width 。這兩種用法不影響下文的介紹。

那么這兩種方式有什么區別呢?

從代碼書寫的角度來說,都是通過一個資源名稱(resource_name)來訪問資源。我們反編譯一下 apk,看看編譯后是什么樣的。

分別在項目 app module、library module、xml 中編寫如下代碼

圖片

我們反編譯一下 apk,看看這三種代碼在 apk 中是如何表現的。

圖片

可以發現 appTest 方法和 xml 中的資源變成了數字(0x7f0e0069),libTest 方法中的資源依舊是通過 Lcom/bytedance/lib/R$string;->test 訪問的

結論:

  • 主 module 中引用的資源被編譯成了數值;
  • 子 module、aar 中通過 R 的內部類間接引用數值;
  • xml 中的資源 id 全部編譯成了數值。(看上圖中 xml 的屬性—— lay out_width 等依舊是字符串,其實它背后也是資源 id 數值,這塊的字符串其實是沒有用的,甚至在一些包體積優化中可以直接去掉)。

那么為什么 libTest 方法中是通過 field 引用,而 appTest 中就變成數字了呢?

2.3 Android 中資源編譯的簡單流程

假設有一個工程,只有一個 app module,通過 maven 倉庫依賴若干三方 aar,項目編譯時的簡化流程如下圖:

圖片

  1. 下載三方 aar;
  2. 將 app module 和三方 aar 中的資源經過 aapt2 進行編譯、鏈接,最終生成R.jar和ap_
  • R.jar 包含了最終打入 apk 的所有 R.class,每個依賴對應一個。aapt2 也會默認按照字母排序為每個資源分配唯一的 id 值。注意:新增刪除一個資源都會導致它后面的資源 id 改變。aapt2 允許通過配置干預 id 的分配。
  • ap_ 文件中包含了所有編譯好的資源文件。
  1. App module 的 java 文件與 R.jar 一起被 javac 編譯。由于 R.jar 中的 field 都是 final,因此 app module 中通過 R 引用的資源全部被內聯成了數值。而三方 aar 中由于已經是 class,無需進行編譯,因此依舊是通過 R 引用來使用資源;
  2. 最后把 app module 編譯出來的 .class、三方 aar 中的 .class 轉成 dex,與 ap_ 一起壓縮到 apk 中。

因此就很容易理解為啥 libTest 中依舊是通過 R 來使用資源,而 appTest 中通過數值直接引用(被內聯)。

libTest module 雖然被 app module 通過源碼依賴,但是在資源編譯這塊其實是類似的,這里不展開介紹。

2.4 總結

Android 中的資源的無論是通過 java 代碼使用還是 xml 使用,最終都是通過資源 id 值進行查找的。

把 apk 拖到 as 中,查看 resources.arsc 文件,可以看到它里面包含了 apk 中所有資源的 id 索引,以及該資源名對應的真正資源或值。很容易想到,App 運行起來也是通過資源 id 值經過這個資源表來查找真正的資源內容。

3. 插件使用宿主資源

3.1 插件如何使用宿主資源

想象一下,我們想要把 App 的直播功能做成一個插件動態下發,直播功能所需要的大部分資源都在直播插件中,但是總有一些資源來自宿主,如一些通用的 UI 組件中包含的資源(support/androidx 庫)等。

那么,假設宿主中有一張圖片名為 icon,直播插件中的 xml 通過 @drawable/icon 引用了這張圖片,同時也在代碼中通過 R.drawable.icon 引用了它,實際直播插件中是沒有 icon 這張圖片的,它存在于宿主中。宿主編譯完后,按照前面的知識點,宿主中的 icon 對應的數值被編譯成 0x7f010001。

插件本身也是一個 apk,根據前面介紹的知識點,插件編譯完成后,xml 中的 @drawable/icon 會編成一個數值(0x7f010001),java 代碼中的 R.drawable.icon 也會直接或間接編成一個數值(0x7f010001)。當這個插件運行在宿主上,按照前面的介紹,插件會去查找 0x7f010001,發現可以找到,這樣就正確的使用了宿主資源。

插件編譯時我們會做一些處理,使插件中可以引用到宿主 id。

3.2 插件使用宿主資源有什么問題

前文介紹過,新增或刪除一個資源都可能導致其他許多資源的 id 被改變。

我們的宿主編譯出來后 icon 為 0x7f010001,基于已有的宿主編譯出一個插件后,插件中引用的 icon 也是 0x7f010001,此時沒什么問題。

宿主迭代后,新增了一個新的資源 aicon,按照前面介紹的資源 id 分配規則,新版本的宿主中 aicon 的 id 值為 0x7f010001,icon 的 id 值被分配為 0x7f010002。老版本的插件下發到新版本的宿主上時依舊會通過 0x7f010001去宿主中找 icon,自然就找錯了。運氣好一點可能只是圖片展示異常,運氣不好點可能就直接 crash 了。

3.3 如何解決這類問題

為了解決這個問題,業界目前有一個通用、穩定的方案——資源固定。宿主編譯時通過 aapt2 提供的參數對插件使用到的資源進行固定,使宿主每次打包時這些資源的值永遠不發生改變。

資源固定方案的弊端:

  1. 一個插件對應一個宿主的情況:
  • 必須把宿主的所有資源都進行固定。如果只固定插件使用的資源,當一個宿主有兩個插件時,兩個插件各自給宿主固定自己需要的資源,在代碼合并時,很容易引發沖突,因為資源固定的值是不允許重復的;
  • 當宿主接入多個涉及到資源固定的框架,如:插件化、資源熱修復、游戲重打包框架等,這些框架之間進行資源固定時也需要考慮統一固定,這個成本是很高的;
  • 資源固定提高了宿主接入框架的成本。
  1. 一個插件運行在多個宿主的情況:
  • 當一個插件想要運行在多個宿主上,就需要每個宿主針對該插件的資源使用情況進行資源固定。一旦某個宿主已經對某個資源進行了固定,導致其與該插件要求的資源固定產生沖突,插件就需要對該宿主進行妥協,根據該宿主已有的資源固定重新生成固定規則。這樣就無法實現一個插件在多個宿主上運行。我們目前有一個需求:同一個插件需要在上千個宿主上運行,如果不能解決這個問題,可能需要打成百上千個插件出來,很明顯是不合理的;

  • 資源固定提高了宿主接入框架的成本。

為了解決上述的問題,我們研究了一套新的方案解決資源錯亂問題。

4. 免資源固定方案

同一個版本的插件運行在不同版本甚至不同的 App 上時,插件的代碼是固定的,而宿主中的資源 id 是會改變的,為了解決資源錯亂問題,當前的思路是保證宿主每次出新版本時資源 id 不變。那么有沒有辦法在不約束宿主的情況下,讓插件始終跟宿主的資源 id 保持一致呢?

由于插件打包時,宿主是未知的,并且對于一個插件跑在多個宿主的情況,宿主也是多樣的。所以沒法指定讓插件把 id 打成滿足宿主的樣子,而前文也介紹過,插件中引用宿主 id 的地方都是常量。那怎么辦呢?

是否可以在插件運行到宿主上時,動態修改插件中的內容,實現插件與宿主 id 值匹配的效果。

比如插件中使用了宿主的資源 icon,對應的 id 值為 0x7f010001。當該插件運行在一個 icon 為 0x7f010002的宿主上時,由于運行時資源查找都是通過 id 值進行的,此時我們只能知道插件是在找一個 id 為 0x7f010001 的資源。通過某些手段,如果我們可以把 0x7f010001 映射成 icon 這個字符串,然后利用 Android 系統提供的Resources#getIdentifier方法,動態獲取到當前宿主中 icon 對應的資源 id,即可保證插件加載到正確的資源。

這個工作需要在插件編譯時、運行時分別做一些工作配合完成實現。

4.1  插件編譯時工作

本小節內容基于 agp4.1 介紹,各個版本有些許差異,但總體思路大同小異。

前面介紹了,插件使用宿主資源主要有兩種情況:1.通過 java 代碼 2.通過 xml。

4.1.1 處理 java 代碼中引用宿主的資源

java 代碼在編譯成 class 之后,對于引用宿主資源 id 的代碼,有的會編譯成數值,有的依舊是通過 R 引用。對于后者,我們可以很容易找出來,對于前者就有些困難了,因為單純去掃描 class 中 0x7f 開頭的數字,很容易誤判,把一個無意義的數字也當作資源 id 處理。

前面講了為什么 class 中的資源 id 會內聯成數值,那我們不讓它內聯不就好了嗎?只需要在編譯過程中處理 R.jar,移除 class 中所有的 final 字段,就可以保證插件中引用宿主的資源 id 全部通過 R 進行引用。

這塊需要對 agp 的工作流程、gradle plugin 的開發有一定的了解,用到了 asm 字節碼修改技術和 agp 提供的 transform api,不了解的同學可以單獨查一下,這塊就不詳細介紹了。

簡單來說就是通過這兩項技術,可以在編譯 apk 時,對 class 文件進行修改。

開始實踐

  1. 由于 R.jar 是在 processResourcesTask 中生成的,因此可以寫一個 gradle plugin,在 processResourcesTask 的 doLast 中獲取到 R.jar,修改 R.jar 中的字節碼,將 field 中的 id 為 0x7f 開頭的字段的 final 修飾符全部移除。這樣就可以保證插件 class 中所有引用宿主資源的地方都不會被內聯成數值;
  2. 經過第一步的處理,插件中引用的宿主資源全部通過 R.xx.xx 來引用,但插件 R 中的數值依舊是無法與宿主對應的。因此我們繼續寫一個 transform,掃描出插件中通過 R 引用資源的地方,利用 asm 將其從原來的 R 引用修改為方法調用。插件運行時,原本類似 R.drawable.test 的代碼不再是獲取一個常量數值,而是調用一個方法,內部動態計算當前宿主中對應的值。?

圖片

總結:

以上,通過編譯時的一些處理,即可解決插件 java 代碼中引用宿主資源時免資源固定的問題。

  • 優點:無需資源固定。
  • 缺點:
  1. 插件中的部分資源不進行內聯,會使包體積有非常微小的增加,但是問題不大;
  2. 插件引用宿主資源由原來的常量變成了方法調用,執行效率降低,不過這塊可以通過緩存來解決。同時插件化本身就是一項黑科技技術,有時候犧牲一些性能,解決一個問題還是非常值得的。

4.1.2 處理 xml 代碼中引用宿主的資源

xml 中引用宿主資源的問題僅靠編譯時是無法解決的,因為 xml 不像 java 代碼一樣可以執行邏輯,前面介紹了,xml 在編譯結束后,資源全部編成了數值,而我們在編譯時又無法知道未來運行在哪個宿主,值為多少。所以修改 xml 中資源id的工作只能搬到運行時去搞。當然也需要在編譯時做一些事情,輔助運行時的修改操作。

運行時我們需要修改 apk 的 xml 中 0x7f 開頭的資源,將其數值改為對應當前宿主的正確數值,而通過 xml,我們只能拿到一個數值,因此我們可以在插件編譯時收集插件 xml 中使用的宿主資源所在的 xml 文件以及它們所對應的資源 name,運行時借助前文提到的mapRes方法即可獲取到需要被修改后的值。

開始實踐

前文介紹過,aapt2 編譯/鏈接后會生成一個 ap_ 文件,這個文件中包含了最終會進入插件中的所有編譯后的資源(包括各種 xml、resources.arsc、AndroidManifest.xml ),我們只需要分析這些文件中引用的 0x7f 開頭的資源,根據 R.txt(aapt2生成的一個文件)找到對應的資源名,將資源名、id 值、所在文件記錄到一個文件中,一并打包進插件 apk 中。

至于如何掃描這些文件中 0x7f 的資源,我們在不同階段使用了不同方式,大家可以自行選擇:

  1. 使用 aapt2 命令 dump 文件信息,分析 dump 后的文本內容(我們編譯時是這么做的,簡單粗暴、性能較差、不夠優雅);
  2. 根據文件格式分析對文件內容進行解析,找到 0x7f 開頭的資源(比較優雅,效率也高,我們運行時是這樣做的)。

總結:

以上,便生成了一個文件,內部存儲了插件 xml 中使用到的宿主資源的信息。大概長下面這樣:

圖片

前文一直在說 xml 中使用的宿主資源,看上面這個配置文件發現 fileNames 中怎么會有 resoureces.arsc ?它明明不是 xml 文件?

其實 Android 資源編譯之后,values 相關的一些資源文件都不存在了,會直接進入到 resources.arsc 中,layout 這類文件還存在,resoureces.arsc 中 layout 指向的正是各種 layout.xml,而 string 等 value 類型的資源指向的是一個真實的內容。感興趣的同學可以通過 Android Studio 打開 apk,觀察一下 resources.arsc 中的結構。

4.2 插件安裝時的工作

前面介紹了在插件編譯時,給 java 代碼中插入了一些邏輯,實現了插件動態根據宿主環境獲取資源 id 的效果。但是 xml 編譯完之后,資源 id 都直接編譯成了數字,xml 中也無法插入邏輯,因此我們只能在插件運行前,根據宿主環境進行修改。

插件在宿主中運行前都有一個插件安裝的過程,類似于 apk 在 Android 系統中的安裝,因此只需要在每次插件安裝前,或者宿主升級后,根據編譯時生成的配置文件,結合 mapRes 方法,對插件中的 xml、resources.arsc 文件進行修改即可。

確定了修改時機和修改內容,接下來就要詳細介紹怎么修改這些文件了。

5. 修改 apk 中的資源文件

5.1 如何修改 xml、arsc 文件

Android 中的 layout、drawable、AndroidManifest 等文件在編譯成 apk 后,不再是常規的 xml 文件了,而是新的一種文件格式 Android Binary XML,我們這里稱之為 axml。那么如何修改 axml 文件呢?

所有的文件都有自己的文件格式,程序在讀取文件時都是讀的 byte 數組,然后根據文件格式解析 byte 數組中每一個元素的含義。因此我們只需要了解了 axml 的文件格式,按照規范解析這個文件,在 byte 數組中找到其中表示資源 id 的位置,將原本的資源 id 根據 resMap 方法映射出新的值,然后修改 byte 數組中對應的部分。(非常幸運,我們這里修改的只是 axml 文件中的一個 8 位 16 進制數,這個修改不會導致文件中內容的長度、偏移等信息改變,因此直接替換對應部分的 byte 數組即可。)

resources.arsc 是 apk 的資源索引表,里面記錄了 apk 中所有的資源,對于 values 類型的資源,資源對應的內容會全部進入到 resources.arsc 中,因此我們也需要對這個文件進行修改(如一個 style 的 parent 是宿主資源,我們就需要修改它)。修改的方法和 xml 類似,只需要按照規范解析 byte 數組,找到要修改內容的偏移量,替換即可。

關于 axml、arsc 的文件格式,網上有很多文章介紹,這里就不詳細敘述了。

Apktool 是一款強大、開源的逆向工具,它可以把 apk 反編譯成源碼,那它肯定也有讀取 apk 中 axml、arsc 的代碼,不然怎么輸出一個可以編輯的 xml 源碼文件?所以我們可以直接去扒 apktool 中讀取 axml、arsc 的代碼,當讀取到 axml 中屬于宿主的 id 時,記錄一下 byte 數組的偏移量,直接替換對應位置的 byte 子數組。

aapt2 為我們提供了 dump 資源內容的能力,可以幫助我們直接用“肉眼”去看 axml、arsc 的內容,借助這個工具可以讓我們很方便的確認修改內容,驗證修改是否生效。以 30.0 版本的 build-tools 中的 aapt2 為例,它的命令為aapt2 dump apk路徑 --file 資源路徑?。后面不跟--file 資源路徑,會直接 dump arsc。

以下是 dump 出來的 arsc,可以看到最后一個 style 的 parent 是一個 0x7f 開頭的宿主資源。

圖片

以下是 dump 出來的 activity_plugin1.xml,可以看到 TextView 中引用了一個宿主中的資源作為 backgroud。

圖片

5.2 修改 apk 中的 xml/arsc 文件

以上我們知道了如何修改一個 axml、arsc 文件。插件安裝時我們拿到的是 apk 文件,那么如何修改 apk 中的 axml、arsc 文件呢?

5.2.1 重壓縮方式修改

Apk 其實就是一個 zip 文件,修改 apk 中的文件內容,首先想到的最簡單的方法就是讀取 zipFile 里面的文件,修改之后重壓縮。

java 為我們提供了一套操作 zipFile 的 api,我們可以輕松的將 zip 文件中的內容讀取到內存,在內存中修改之后利用 ZipOutputStream 重新寫入到新的 zipFile 中。

代碼實現非常簡單。修改成功后,測試發現是可行的,那我們的第一步就算是成功了,說明運行時動態修改插件的路子是行的通的。

竊喜之于,發現修改過程十分耗時。以公司的直播插件為例(直播插件大約 30 MB,屬于比較大的插件了),在 9.0 及其以上的設備上耗時約 8s,在 7~8 的設備耗時大約 20~40s,在 7.x 以下設備大約耗時 10~20s。盡管插件安裝是在后臺進行,適當的增加一些時間是可以接受的,但是幾十秒的耗時很明顯不可以接受。那我們只能想別的辦法了。

關于各個版本的耗時差異:

Android7.0 開始,官方使用 ZLIB 來提供 Deflater、Inflater 的實現,優化了解壓壓縮算法速度(可以查看 Deflater.java、Inflater.java 的注釋)。但是 7.x/8.x 的 ZipFileInputStream 在讀取數據時有一個 8192 的 BUFSIZE 限制( 8.x 之后移除了這個限制),導致在讀取數據時循環次數增多,效率反而下降。

7.0 開始,ZipFileInpugStream 在讀取數據時是通過 native 方法 ZipFile_read 進行的。以下是 android8.0 和 android9.0 中 ZipFile_read 的部分代碼。

圖片

5.2.2 直接修改 apk 的 byte 數組

Apk 其實就是一個 zip 文件,關于 zip 文件的介紹可以參考 Zip 的官方文檔。

簡單總結一下,zip 文件是由數據區、中央目錄記錄區、中央目錄尾部區組成(高版本的 zip 文件增加了新的內容)。

  • 中央目錄尾部區:通過尾部區我們可以知道 zip 包中文件的數目、中央目錄記錄區的位置等信息;
  • 中央目錄記錄區:通過尾部區我們可以快速找到中央目錄記錄區中的每一條文件記錄,這些記錄主要描述了 zip 包中文件的基本屬性如文件名、文件大小、是否壓縮、壓縮后的大小、文件在數據區中的偏移等;
  • 數據區:數據區用來存放文件真實的內容,根據中央目錄記錄區記錄的內容,可以快速在數據區找到對應的文件元數據以及文件的真實數據(如果壓縮,則是壓縮后的數據)。

開始干活

了解了 zip 文件的格式后,我們只需要按照文件格式協議,在 apk 中找到我們需要修改的文件數據在 apk 中的偏移量,然后結合前面修改 axml/arsc 文件的方式,直接修改對應的 byte 數組即可。借助 java 為我們提供的 RandomAccessFile 工具,我們可以快速的文件的任意位置進行讀取/寫入。

修改過程中發現,apk 中的 xml 文件大部分是被壓縮的( res/xml 目錄下的一般不壓縮),這就導致我們從 apk 中拿出來的 byte 數組是 axml 被壓縮后的數據,我們要對這段數據進行修改,需要先利用 Deflate 算法對它進行解壓( zip 文件中一般都是用的 Deflate 算法),然后進行修改再壓縮,但是經過我們修改后,可能重新壓縮出來的數據就與修改前的數據長度不匹配了,如果是縮短還好,修改一下文件元數據即可,如果文件長度變長可能會導致后面文件的偏移量都要改變,牽一發而動全身。

好在插件的打包過程我們是可以侵入的,前面介紹“插件編譯時工作”時,我們在編譯時拿到了需要修改的文件,因此我們只需要控制 apk 打包時不要對這些文件進行壓縮(事實上 Android Target30 也要求 arsc 文件不進行壓縮)。這樣就很簡單的解決了問題,當然會導致插件包體積的增加。

最終測試在直播插件中,開啟這個功能會導致包體積增加 20kb,對于接近 30mb 體積的直播插件來說,這個增量是可以接受的,而且也不會影響宿主包體積。(這個增量取決于插件有多少 xml 使用了宿主資源,一般插件的增量應該都是小于直播插件的。)

改造完成后,經測試,直播插件在各個版本手機上修改時長大約在 300~700ms 之間,修改速度提升了 10~90 倍。大部分插件也比直播插件小,耗時可以保證在 100ms 之內。同時這個修改過程僅在插件第一次安裝或者宿主升級時做,并且是在后臺完成,所以是完全可以接受的。

責任編輯:未麗燕 來源: 字節跳動技術團隊
相關推薦

2014-11-06 10:31:55

移動營銷

2010-09-16 09:26:57

CSS display

2018-07-25 09:37:53

數據中心利用率預測

2013-09-12 10:21:07

Nubo虛擬化MDM

2013-05-16 11:07:37

Android開發Android應用自動化測試

2016-09-22 21:42:48

Android鬧鐘移動

2012-03-27 22:22:51

iMC基礎IT資源管理

2022-08-12 13:26:14

內聯崩潰TV 端插件化

2012-06-20 14:31:44

2011-08-03 10:26:13

Oracle指定nowait

2010-01-27 15:36:35

Android錄音失真

2014-07-17 00:42:18

Android應用測試方案

2020-09-08 11:06:04

機器學習

2015-03-18 10:35:13

虛擬化監測虛擬化策略虛擬化解決方案

2010-06-02 10:21:56

Windows 7虛擬化

2021-02-22 18:08:38

農業物聯網IOT

2014-11-07 14:30:09

統一通信

2012-05-27 16:21:31

IDC華為

2018-12-03 12:17:27

Semptian解決方案

2016-03-13 17:49:41

點贊
收藏

51CTO技術棧公眾號

国产精品三级视频| 国产一区二区三区四区三区四| 欧美日韩一区二区三区 | 亚洲日本va午夜在线电影| 亚洲一区二区在线观看视频| 欧美在线视频二区| 国产麻豆免费观看| 日韩午夜激情| 久久精品99久久久香蕉| 亚洲av成人片无码| 国产一区高清| 亚洲成人免费在线观看| 亚洲高清视频在线观看| 色婷婷av一区二区三| 全国精品久久少妇| 97超级碰在线看视频免费在线看| 久草福利资源在线| 免费欧美一区| 精品免费99久久| 国产成年人视频网站| 人狥杂交一区欧美二区| 亚洲人成在线观看一区二区| 在线亚洲一区二区| 日韩精品一区二区三区四区| 亚洲熟妇无码av在线播放| 国产在线资源| av一区二区三区黑人| 国产中文欧美精品| 国产伦精品一区二区三区视频网站| 欧美精品激情| 久久手机精品视频| 美女av免费看| 九九热线有精品视频99| 精品久久五月天| 亚洲第一天堂久久| 久久精品黄色| 欧洲一区二区av| 116极品美女午夜一级| heyzo高清在线| 一区二区三区美女| 伊人再见免费在线观看高清版| 啊v视频在线| 国产欧美一区二区三区网站| 久久久久久国产精品一区 | 黄色日韩网站视频| 国产精品视频永久免费播放| 无码人妻精品一区二区三区不卡 | 欧美一级片免费| 国产东北露脸精品视频| 成人激情综合网| 国产精品福利电影| 国产主播一区二区| 亚洲va久久久噜噜噜| 国产情侣自拍小视频| 国产一区中文字幕| 97超级在线观看免费高清完整版电视剧| 一级黄色大片网站| 精品一区二区久久久| 国产在线拍揄自揄视频不卡99 | 最近高清中文在线字幕在线观看1| 亚洲成人免费看| 日韩国产欧美亚洲| gay欧美网站| 欧美伊人久久久久久久久影院| 男人的天堂日韩| 国产精品蜜月aⅴ在线| 欧美性感一类影片在线播放| 亚洲最大天堂网| 欧美成人精品午夜一区二区| 精品成人佐山爱一区二区| 香港三日本8a三级少妇三级99| 麻豆一区二区| 亚洲天堂男人的天堂| 亚洲精品天堂网| 欧美激情第10页| 69视频在线播放| 国产偷人爽久久久久久老妇app| 蜜桃视频在线观看一区二区| 91精品在线观看视频| 亚洲精品第五页| 久久综合九色综合欧美亚洲| 亚洲人一区二区| 污片视频在线免费观看| 神马电影网我不卡| 99精品视频在线免费观看| 裸模一区二区三区免费| 亚洲麻豆精品| 亚洲风情在线资源站| 亚洲国产精品毛片av不卡在线| 久久亚洲国产精品尤物| 欧美成人性战久久| 麻豆精品免费视频| 欧美在线视屏| 日韩免费av一区二区| 国产乱人乱偷精品视频| 91免费看片在线观看| 在线天堂一区av电影| av中文在线资源库| 欧美美女一区二区三区| 久久福利小视频| 国产精品99一区二区三区| 97在线视频精品| 国产精品高潮呻吟av| 久久久久久免费网| 成年人深夜视频| 欧美风情在线视频| 亚洲免费视频观看| 久久亚洲成人av| 久久电影网站中文字幕| 国模精品一区二区三区| 高h视频在线观看| 91国产免费看| 欧美大片免费播放器| 亚洲精品888| 国产精品入口福利| 天堂中文字幕在线| 亚洲综合图片区| 色婷婷.com| 精品国产123区| 91av网站在线播放| 亚洲精品第五页| 亚洲精品国产成人久久av盗摄| 久久亚洲一区二区三区四区| 国产精品免费一区二区三区在线观看| 91ph在线| 在线观看欧美日本| 人妻无码一区二区三区| 黄色成人精品网站| 成人亚洲综合色就1024| 成a人v在线播放| 欧美午夜宅男影院在线观看| 日本精品一二三区| 91精品国产乱码久久久久久 | 亚洲黄色三级视频| 懂色av一区二区三区免费看| 亚洲小说欧美另类激情| 成人亚洲视频| 中文字幕九色91在线| 国产99免费视频| 国产午夜亚洲精品午夜鲁丝片| 黄色动漫在线免费看| 国产成人福利av| 韩国一区二区电影| 色wwwwww| 午夜精品视频一区| 性囗交免费视频观看| 亚洲另类视频| 久热这里只精品99re8久| 天堂中文av在线资源库| 亚洲九九九在线观看| 中文字幕视频网站| 久久久久99精品一区| 日本999视频| 久久理论电影| 成人中心免费视频| 在线不卡日本v二区707| 欧美va在线播放| 国产在线观看你懂的| 91在线精品一区二区三区| 欧美成人高潮一二区在线看| 亚洲国产国产| 国产精品第一区| 午夜在线视频| 日韩一区二区在线观看视频 | 男生草女生视频| 蜜桃视频第一区免费观看| 色呦呦网站入口| eeuss鲁片一区二区三区| 97在线视频观看| h网站视频在线观看| 宅男噜噜噜66一区二区66| 一本一道久久a久久精品综合| 国产亚洲精品女人久久久久久| 免费成人在线视频观看| 中文字幕成人一区| 9l视频自拍九色9l视频成人| 欧洲亚洲妇女av| 日韩伦理在线观看| 欧美xfplay| 国产美女www爽爽爽| 亚洲乱码中文字幕综合| av在线网站观看| 精品在线亚洲视频| 无码中文字幕色专区| 欧洲福利电影| 国产成人一区二区三区免费看| 在线观看福利电影| 精品国产一区二区三区在线观看| 囯产精品一品二区三区| 91国在线观看| 久草免费在线视频观看| 国产午夜精品一区二区三区视频| 91大神免费观看| 免费亚洲婷婷| 91免费国产精品| 欧美限制电影| 国产欧美丝袜| 国产精品xnxxcom| 日本久久久久久| 污片视频在线免费观看| 在线看国产精品| 婷婷五月综合激情| 91精品国产综合久久精品| 中文字幕高清在线免费播放| 一区二区三区在线视频免费 | 91成人在线观看国产| 成人免费看片| 在线播放日韩欧美| 无码精品视频一区二区三区| 日韩欧美的一区| 中文字幕在线2019| 一本色道久久综合精品竹菊| 国产精品6666| 亚洲美腿欧美偷拍| 蜜桃av.com| 久久精品一区二区三区不卡牛牛 | 日本一区二区久久| 少妇一级淫免费观看| 国产精品亚洲成人| jizz大全欧美jizzcom| 三级不卡在线观看| 免费av手机在线观看| 欧美日韩免费| 97精品国产97久久久久久粉红| 精品视频日韩| 日本一区二区视频| 香蕉人人精品| 精品国产综合久久| 国产ts一区| 国产亚洲自拍偷拍| 国产成人一二| 国产在线播放一区二区| 国产精品xxxav免费视频| 91gao视频| 激情综合婷婷| 91视频最新| 久久伊人久久| 91超碰在线免费观看| vam成人资源在线观看| 成人黄色网免费| 久久精品国产精品亚洲毛片| 国产一区二区视频在线观看| 九九久久国产| 成人欧美一区二区三区在线| 99视频有精品高清视频| 亚洲精品女av网站| 日韩欧美中文在线观看| 亚洲最大的成人网| 中文在线综合| 国产欧美一区二区视频| 久久婷婷国产| 欧美另类高清视频在线| 一个色免费成人影院| 亚洲v国产v| 久久大综合网| 成年在线观看视频| 亚洲日本国产| 国产精品99久久免费黑人人妻| 爽好久久久欧美精品| 深夜黄色小视频| 国产一区三区三区| 久久性爱视频网站| 久久久久综合网| 国产免费嫩草影院| 一区二区三区.www| 99久在线精品99re8热| 在线免费观看日韩欧美| 一个人看的www日本高清视频| 欧美一级在线视频| 日韩在线一区二区三区四区| 亚洲欧美综合精品久久成人| 欧美成人三区| 97在线视频一区| av在线一区不卡| 91久久精品一区二区别| 老司机成人在线| 亚洲精品欧美精品| 欧美日韩1080p| 免费观看成人在线视频| 狠狠狠色丁香婷婷综合激情 | 欧美午夜精彩| 青青在线视频免费观看| 久久性天堂网| 少妇丰满尤物大尺度写真| 久久―日本道色综合久久| 日本黄色免费片| 亚洲成人动漫一区| 在线观看日批视频| 日韩av中文字幕在线播放| 免费人成在线观看播放视频| 国内免费久久久久久久久久久| 欧美free嫩15| 国产区日韩欧美| 国产精品久久久久无码av| 黄页网站在线观看视频| 精品一区二区免费| 中日韩精品一区二区三区| 亚洲男人电影天堂| 无码人妻aⅴ一区二区三区有奶水| 日韩免费观看高清完整版在线观看| 国产综合在线观看| 国产69精品久久久久99| 99国内精品久久久久| 欧美精品二区三区四区免费看视频| 国内在线观看一区二区三区| 亚洲成人天堂网| 久久久五月婷婷| 精品视频在线观看免费| 8v天堂国产在线一区二区| 久久视频www| 97免费在线视频| 日本精品一区二区三区在线观看视频| 日韩高清av电影| 一本久久知道综合久久| 粗大的内捧猛烈进出视频| 中文字幕不卡一区| av图片在线观看| 亚洲大胆美女视频| 国产超级va在线视频| 国产在线高清精品| 成人羞羞视频在线看网址| 久草资源站在线观看| 成人一区二区三区在线观看| 国产精品国产精品88| 欧美精品 日韩| 午夜激情在线观看| 国产免费成人av| 色综合狠狠操| 一起操在线视频| 国产精品免费视频网站| 国模私拍一区二区| 国产一区二区三区视频| 向日葵视频成人app网址| 欧美日韩在线不卡一区| 性欧美xxxx大乳国产app| 三级电影在线看| 日韩欧美福利视频| 免费在线视频你懂得| 国产成人精品视| 不卡在线一区| 亚洲精品手机在线观看| 日韩一区欧美一区| 国产伦理吴梦梦伦理| 美日韩精品免费观看视频| 日韩精品一级| 国产va亚洲va在线va| www.视频一区| 亚洲GV成人无码久久精品| 国产视频一区在线| 国精产品一区二区三区有限公司| 亚欧精品在线| 国产一二精品视频| 国产精品99精品| 日韩精品欧美国产精品忘忧草| 国产精品专区免费| 天堂av一区二区| 极品美女销魂一区二区三区| 乱h高h女3p含苞待放| 欧美va亚洲va| 美女100%一区| 一本一道久久久a久久久精品91| 国产一区二区三区不卡在线观看| 国产一级片免费看| 亚洲美女久久久| 久久日本片精品aaaaa国产| 91免费版看片| 久久这里只有精品首页| 最近日韩免费视频| 欧美精品一区二区三区国产精品| 国产精品一区二区三区美女| 黄色a级片免费| 一区在线中文字幕| 色哟哟国产精品色哟哟| 国产精品1234| 欧美高清日韩| www.色天使| 91精品国产一区二区三区| 国产盗摄精品一区二区酒店| 欧美成人在线免费观看| 精品一区二区日韩| 亚洲天堂一区在线观看| 色婷婷综合久久久久中文字幕1| 北条麻妃一区二区三区在线观看| 国产成人a亚洲精v品无码| ...av二区三区久久精品| 三级网站在线看| 91日韩在线视频| 亚洲一区二区成人| 熟女少妇a性色生活片毛片| 亚洲高清av在线| 亚洲国产91视频| 国产日产欧美视频| 亚洲欧美日韩电影| 免费在线高清av| 福利精品视频| 久久超碰97中文字幕| 欧美性猛交bbbbb精品| 久久91超碰青草是什么| 精品日韩免费| 自拍视频一区二区|