成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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 之內。同時這個修改過程僅在插件第一次安裝或者宿主升級時做,并且是在后臺完成,所以是完全可以接受的。

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

2010-09-16 09:26:57

CSS display

2014-11-06 10:31:55

移動營銷

2013-09-12 10:21:07

Nubo虛擬化MDM

2013-05-16 11:07:37

Android開發Android應用自動化測試

2018-07-25 09:37:53

數據中心利用率預測

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

2018-12-03 12:17:27

Semptian解決方案

2012-05-27 16:21:31

IDC華為

2016-03-13 17:49:41

2014-11-07 14:30:09

統一通信
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 精品国产一级片 | 久久精品亚洲一区 | 狠狠色狠狠色综合系列 | 精品1区2区 | 午夜久久久久久久久久一区二区 | 成年人在线观看视频 | 欧美视频免费在线 | 免费黄色的视频 | 国产在线一区二区 | 欧美一区二区三区的 | 欧美日韩亚洲一区 | 亚洲区一区二区 | 日韩电影在线一区 | 亚洲成av片人久久久 | 欧美福利影院 | 久久久国产一区二区三区 | 亚洲精品在线免费观看视频 | 99久久99 | 亚洲一区二区久久 | 国产精品久久av | 永久av | 亚洲va中文字幕 | 国产日韩一区 | 国产91在线观看 | 久久国产精品99久久久大便 | 国产精品久久久久久久免费观看 | 亚洲 中文 欧美 日韩 在线观看 | 午夜精品福利视频 | 欧美一区二区三区久久精品 | 国产成年人视频 | 久久99深爱久久99精品 | 欧美激情久久久久久 | 亚洲精品乱码8久久久久久日本 | 亚洲啊v| 天天拍天天操 | 亚洲一区二区三区免费在线观看 | 四虎av电影 | 久久精品久久久久久 | 国产小视频自拍 | 在线成人| 精品国产不卡一区二区三区 |