<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 包體積優(yōu)化實(shí)踐!

          共 17526字,需瀏覽 36分鐘

           ·

          2022-06-15 18:34



           安卓進(jìn)階漲薪訓(xùn)練營(yíng),讓一部分人先進(jìn)大廠

          大家好,我是皇叔,最近開(kāi)了一個(gè)安卓進(jìn)階漲薪訓(xùn)練營(yíng),可以幫助大家突破技術(shù)&職場(chǎng)瓶頸,從而度過(guò)難關(guān),進(jìn)入心儀的公司。


          詳情見(jiàn)文章:沒(méi)錯(cuò)!皇叔開(kāi)了個(gè)訓(xùn)練營(yíng)



          來(lái)源:公號(hào) 群英傳

          ?

          作者簡(jiǎn)介
          muye,貨拉拉客戶端架構(gòu)師,貨拉拉App Android端技術(shù)負(fù)責(zé)人,在Android App性能優(yōu)化、穩(wěn)定性提升等方向有豐富經(jīng)驗(yàn)

          ?

          背景介紹

          為什么要做包體積優(yōu)化?主要出于以下幾方面的考慮

          1. 下載轉(zhuǎn)化率
            (1)很多應(yīng)用市場(chǎng)流量保護(hù)限制是40M
            (2)很多大型 App 一般都會(huì)有一個(gè) Lite 版本的 App,也是出于下載轉(zhuǎn)化率方面的考慮

          2. 對(duì)app性能的影響
            (1)安裝時(shí)間:比如 文件拷貝、Library 解壓,并且,在編譯 ODEX 的時(shí)候,特別是對(duì)于 Android 5.0 和 6.0 系統(tǒng)來(lái)說(shuō),耗費(fèi)的時(shí)間比較久,而 Android 7.0 之后有了 混合編譯,所以還可以接受。最后,App 變大后,其 簽名校驗(yàn) 的時(shí)間也會(huì)變長(zhǎng)。
            (2)運(yùn)行時(shí)內(nèi)存:Resource 資源、Library 以及 Dex 類加載都會(huì)占用應(yīng)用的一部分內(nèi)存。
            (3)ROM 空間:如果應(yīng)用的安裝包大小為 50MB,那么啟動(dòng)解壓之后很可能就已經(jīng)超過(guò) 100MB 了

          3. CDN流量費(fèi)用增加
            安裝包體積越大,單個(gè)apk下載流量越大

          優(yōu)化思路分析

          apk主要由以下4個(gè)部分構(gòu)成
          (1)代碼相關(guān)的dex文件
          (2)資源相關(guān)的resources.arsc和清單文件等
          (3)so相關(guān)的lib目錄下的文件
          (4)系統(tǒng)簽名文件

          所以我們的優(yōu)化思路如下:

          1. 從apk整體優(yōu)化
            (1)插件化
            動(dòng)態(tài)加載安裝包中的部分代碼、資源、so
            (2)動(dòng)態(tài)資源加載
            動(dòng)態(tài)加載安裝包中的資源、so

          2. 代碼相關(guān)的dex文件優(yōu)化
            (1)代碼混淆
            使用更短的混淆字段來(lái)混淆原始的類名、方法名、變量名
            (2)刪除未使用的代碼
            完全沒(méi)使用的代碼、只使用了一小部分卻引入了整體功能的代碼
            (3)刪除重復(fù)的代碼
            完全重復(fù)的代碼、部分方法重復(fù)的代碼
            (4)sdk優(yōu)化
            重復(fù)功能的sdk(比如圖片加載庫(kù))、只使用了部分功能卻引入了完整的sdk
            (5)dex壓縮
            使用更高壓縮率的算法、可以替換成常量的字節(jié)碼
            (6)多dex關(guān)聯(lián)優(yōu)化
            減少跨Dex調(diào)用的冗余信息
            (7)字節(jié)碼優(yōu)化
            方法內(nèi)聯(lián)、常量?jī)?nèi)聯(lián)、優(yōu)化多余賦值指令、getter和setter方法內(nèi)聯(lián)、刪除日志等

          3. 資源相關(guān)的文件優(yōu)化
            (1)shrinkResources
            將項(xiàng)目中沒(méi)有使用的圖片、xml資源文件使用系統(tǒng)自帶的資源替換
            (2)刪除未使用的資源
            (3)刪除重復(fù)資源
            (4)AndResGuard-資源混淆
            使用更短的混淆名來(lái)混淆原始的資源名
            (5)屬性代碼替換shape xml文件
            (6)語(yǔ)言資源優(yōu)化
            去掉多余的國(guó)際語(yǔ)言資源
            (7)圖片資源優(yōu)化
            圖片格式優(yōu)化、圖片壓縮、圖片分辨率優(yōu)化
            (8)本地圖片轉(zhuǎn)網(wǎng)圖
            編譯時(shí)將本地的圖片從apk中刪除,改成網(wǎng)絡(luò)動(dòng)態(tài)加載的方式

          4. so相關(guān)的文件優(yōu)化
            移除多余的so架構(gòu)、移除調(diào)試符號(hào)

          工欲善其事必先利其器,為了實(shí)現(xiàn)我們的優(yōu)化效果,我們引入和開(kāi)發(fā)部分工具、插件來(lái)幫助我們做apk、代碼、資源的相關(guān)分析

          貨拉拉Android 包體積優(yōu)化思維導(dǎo)圖

          image.png

          APK構(gòu)成

          1. APK構(gòu)成
          image.png

          apk各個(gè)部分的詳細(xì)信息:

          image.png
          1. dex簡(jiǎn)介

          Dex 是 Android 系統(tǒng)的可執(zhí)行文件,包含 應(yīng)用程序的全部操作指令以及運(yùn)行時(shí)數(shù)據(jù)。因?yàn)?Dalvik 是一種針對(duì)嵌入式設(shè)備而特殊設(shè)計(jì)的 Java 虛擬機(jī),所以 Dex 文件與標(biāo)準(zhǔn)的 Class 文件在結(jié)構(gòu)設(shè)計(jì)上有著本質(zhì)的區(qū)別。當(dāng) Java 程序被編譯成 class 文件之后,還需要使用 dx 工具將所有的 class 文件整合到一個(gè) dex 文件中,這樣 dex 文件就將原來(lái)每個(gè) class 文件中都有的共有信息合成了一體,這樣做的目的是 保證其中的每個(gè)類都能夠共享數(shù)據(jù),這在一定程度上 降低了信息冗余,同時(shí)也使得 文件結(jié)構(gòu)更加緊湊。與傳統(tǒng) jar 文件相比,Dex 文件的大小能夠縮減 50% 左右


          1. apk打包過(guò)程

          (1)簡(jiǎn)化版

          image.png

          (2)詳細(xì)版

          image.png

          Apk分析工具

          1. zip解壓

          直接把a(bǔ)pk后綴改成zip,然后解壓縮
          解壓之后可以看到apk的各個(gè)組成部分

          image.png
          1. Android Studio自帶的Analyze APK

          直接在Android Studio中點(diǎn)擊打開(kāi)apk文件即可,打開(kāi)之后可以看到各個(gè)部分的大小


          還可以做apk的對(duì)比分析,可以看到新舊版本的體積對(duì)比,更直接客觀的看出新版本哪部分體積增加了及哪部分體積減少了


          另外,還可以點(diǎn)開(kāi)每個(gè)dex文件,查看里面的具體類信息

          1. 反編譯工具APKTool

          APKTool主要包含三個(gè)部分:apktool、dex2jar、jd-gui,作用分別如下:

          • apktool
            作用:資源文件獲取,可以提取出圖片文件和布局文件進(jìn)行使用查看

          • dex2jar 作用:將apk反編譯成java源碼(classes.dex轉(zhuǎn)化成jar文件)

          • jd-gui 作用:查看APK中classes.dex轉(zhuǎn)化成出的jar文件,即源碼文件

          反編譯命令如下:

          java -jar apktool_2.3.4.jar apktool d app-release.apk

          反編譯后可以得到smali碼,通過(guò)jd-gui可以打開(kāi)如下:


          1. class-shark

          (1)android-classshark 是一個(gè) 面向 Android 開(kāi)發(fā)人員的獨(dú)立二進(jìn)制檢查工具,它可以 瀏覽任何的 Android 可執(zhí)行文件,并且檢查出信息,比如類的接口、成員變量等等,此外,它還可以支持多種格式,比如說(shuō) APK、Jar、Class、So 以及所有的 Android 二進(jìn)制文件如清單文件等等

          (2)傳送門(mén)

          https://github.com/google/android-classyshark

          (3)使用方式

          雙擊打開(kāi) ClassShark.jar,拖動(dòng)我們的 APK 到它的工作空間即可。接下來(lái),我們就可以看到 Apk 的分析界面了,這里我們點(diǎn)擊 classes 下的 classes.dex,在分析界面 左邊 可以看到該 dex 的方法數(shù)和文件大小,并且,最下面還顯示出了該 dex 中包含有 Native Call 的類


          (4)點(diǎn)擊左上角的 Methods count 還可以切換到 方法數(shù)環(huán)形圖標(biāo)統(tǒng)計(jì)界面,我們不僅可以 直觀地看到各個(gè)包下的方法數(shù)和相對(duì)大小,還可以看到各個(gè)子包下的方法數(shù)和相對(duì)大小

          1. nimbledroid

          (1)nibledroid 是美國(guó)哥倫比亞大學(xué)的博士創(chuàng)業(yè)團(tuán)隊(duì)研發(fā)出來(lái)的分析 Android App 性能指標(biāo)的系統(tǒng),分析的方式有靜態(tài)和動(dòng)態(tài)兩種方式

          (2)傳送門(mén)

          https://nimbledroid.com/

          (3)靜態(tài)分析:可以分析出APK安裝包中大文件排行榜,Dex 方法數(shù)和知名第三方 SDK 的方法數(shù)及占代碼整體的比例

          (4)動(dòng)態(tài)分析:可以給出 冷啟動(dòng)時(shí)間, 列出 Block UI 的具體方法, 內(nèi)存占用, 以及 Hot Methods, 從這些分析報(bào)告中, 可以 定位出具體的優(yōu)化點(diǎn)

          1. ApkChecker

          (1)簡(jiǎn)介

          ApkChecker是微信APM系統(tǒng)Matrix中的一個(gè)針對(duì)android安裝包的分析檢測(cè)工具

          針對(duì)android安裝包的分析檢測(cè)工具,根據(jù)一系列設(shè)定好的規(guī)則檢測(cè)apk是否存在特定的問(wèn)題,并輸出較為詳細(xì)的檢測(cè)結(jié)果報(bào)告,用于分析排查問(wèn)題以及版本追蹤。

          (2)傳送門(mén)

          https://github.com/Tencent/matrix/wiki/Matrix-Android-ApkChecker

          (3)怎么使用

          Matrix-ApkChecker以一個(gè)jar包的形式提供使用,通過(guò)命令行執(zhí)行 java -jar ApkChecker.jar 即可運(yùn)行

          (4)能統(tǒng)計(jì)啥

          fileSize 列出超過(guò)一定大小的文件,可按文件后綴過(guò)濾,并且按文件大小排序

          --min 文件大小最小閾值,單位是KB

          --order 按照文件大小升序(asc)或者降序(desc)排列

          --suffix 按照文件后綴過(guò)濾,使用","作為多個(gè)文件后綴的分隔符

          countMethod 統(tǒng)計(jì)方法數(shù)

          group 輸出結(jié)果按照類名(class)或者包名(package)來(lái)分組

          checkResProguard 檢查是否經(jīng)過(guò)了資源混淆(AndResGuard)

          findNonAlphaPng 發(fā)現(xiàn)不含alpha通道的png文件

          min png文件大小最小閾值,單位是KB

          checkMultiLibrary 檢查是否包含多個(gè)ABI版本的動(dòng)態(tài)庫(kù)

          uncompressedFile 發(fā)現(xiàn)未經(jīng)壓縮的文件類型(即該類型的所有文件都未經(jīng)壓縮)

          suffix 按照文件后綴過(guò)濾,使用","作為多個(gè)文件后綴的分隔符

          countR 統(tǒng)計(jì)apk中包含的R類以及R類中的field count

          duplicatedFile 發(fā)現(xiàn)冗余的文件,按照文件大小降序排序

          checkMultiSTL 檢查是否有多個(gè)動(dòng)態(tài)庫(kù)靜態(tài)鏈接了STL

          toolnm nm工具的路徑

          unusedResources 發(fā)現(xiàn)apk中包含的無(wú)用資源

          rTxt R.txt文件的路徑(如果在全局參數(shù)中給定了--input,則可以省略)

          ignoreResources 需要忽略的資源,使用","作為多個(gè)資源名稱的分隔符

          unusedAssets 發(fā)現(xiàn)apk中包含的無(wú)用assets文件

          ignoreAssets 需要忽略的assets文件,使用","作為多個(gè)文件的分隔符

          unstrippedSo 發(fā)現(xiàn)apk中未經(jīng)裁剪的動(dòng)態(tài)庫(kù)文件

          (5)檢測(cè)結(jié)果示例

          image.png

          代碼分析工具

          1、Proguard

          proguard代碼混淆時(shí)會(huì)生成dump、mapping、seeds、usage4個(gè)文件,如下:

          以seeds.txt為例,會(huì)列出當(dāng)前混淆規(guī)則下沒(méi)有被混淆的類和成員,為后續(xù)進(jìn)一步混淆優(yōu)化提供指導(dǎo)

          2、lint分析插件

          Android Studio自帶lint分析插件,以分析未使用聲明為例

          Analyze -> Run Inspection by Name -> unused declaration

          此外還可以分析項(xiàng)目中未使用的class和resources資源等

          3、自定義lint分析插件

          除了AS自帶的lint插件,還可以自定義lint插件,自定義lint插件可以掃描以下幾類文件

          (1)JavaScanner / JavaPsiScanner / UastScanner:掃描 Java 源文件

          (2)XmlScanner:掃描 XML 文件

          (3)ClassScanner:掃描 class 文件

          (4)BinaryResourceScanner:掃描二進(jìn)制資源文件

          (5)ResourceFolderScanner:掃描資源文件夾

          (6)GradleScanner:掃描 Gradle 腳本

          (7)OtherFileScanner:掃描其他類型文件

          以掃描java源文件為例,以下掃描項(xiàng)目中所有的Log日志代碼:

          4、自定義gradle插件

          (1)自定義gradle插件的三種方式

          Build script:在build.gradle構(gòu)建腳本中直接使用,只能在本文件內(nèi)使用;

          buildSrc project:新建一個(gè)名為buildSrc的Module使用,只能在本項(xiàng)目中使用;

          Standalone project:在獨(dú)立的Module中使用,可以發(fā)布到本地或者遠(yuǎn)程倉(cāng)庫(kù)供其他項(xiàng)目使用。

          (2)支持的語(yǔ)言

          可以使用多種語(yǔ)言來(lái)實(shí)現(xiàn)Gradle插件,其實(shí)只要最終被編譯為JVM字節(jié)碼的都可以,常用的有Groovy、Java、Kotlin

          (3)Build script示例

          (4)buildSrc project和Standalone project使用

          本質(zhì)上實(shí)現(xiàn)方式一樣

          首先自定義插件類implements Plugin

          實(shí)現(xiàn)「void」 apply(Project project)方法注冊(cè)自定義的Transform類;

          然后自定義類extends Transform,在transform()方法中實(shí)現(xiàn)自定義邏輯,例如可以自定義

          ClassVisitor類訪問(wèn)和修改類的相關(guān)屬性,自定義MethodVisitor類訪問(wèn)和修改方法的相關(guān)屬性

          5、coverage插件

          (1)簡(jiǎn)介

          coverage插件是由字節(jié)跳動(dòng)開(kāi)源的線上無(wú)用代碼分析工具

          (2)原理

          由于代碼設(shè)計(jì)不合理以及keep規(guī)則限制等原因,靜態(tài)代碼檢查無(wú)法找出所有的無(wú)用代碼。

          我們可以從用戶的角度去分析,對(duì)每個(gè)類插樁,執(zhí)行時(shí)將信息上報(bào)到服務(wù)器?;诖罅坑脩羯蠄?bào),用戶沒(méi)有用到的類可以被定義為無(wú)用類。

          在抖音項(xiàng)目中,我們發(fā)現(xiàn)了1/6的無(wú)用類,不包含其引用的資源,共計(jì)3M(dex大小20M),如果能全部刪除,將減少5%包大小

          (3)傳送門(mén)

          https://github.com/bytedance/ByteX/blob/master/coverage/README-zh.md

          6、pmd檢測(cè)重復(fù)代碼

          (1)簡(jiǎn)介

          PMD是一個(gè)靜態(tài)源代碼分析器。它找到常見(jiàn)的編程缺陷,如未使用的變量,空的catch塊,不必要的對(duì)象創(chuàng)建等等。它主要關(guān)注Java和Apex,但支持其他六種語(yǔ)言。

          PMD具有許多內(nèi)置檢查(在PMD術(shù)語(yǔ),規(guī)則中),這些檢查在規(guī)則參考中針對(duì)每種語(yǔ)言進(jìn)行了記錄。我們還支持廣泛的API來(lái)編寫(xiě)您自己的規(guī)則,您可以使用Java或作為自包含的XPath查詢來(lái)執(zhí)行。

          在集成到構(gòu)建過(guò)程中時(shí),PMD最有用。

          (2)支持的4種運(yùn)行方式

          作為Maven的目標(biāo)

          作為Ant任務(wù)

          作為Gradle任務(wù)

          從命令行

          (3)傳送門(mén)

          https://pmd.sourceforge.io/pmd-5.4.1/usage/cpd-usage.html

          (4)檢測(cè)結(jié)果示例

          使用命令行方式:

          ./run.sh cpd --language java --minimum-tokens 100 --files /Users/xxxx/Work/code/DeliciousFood/Classes > ~/Desktop/codeCheck.txt

          7、Simian檢測(cè)重復(fù)代碼

          (1)Simian是一個(gè)可跨平臺(tái)使用的重復(fù)代碼檢測(cè)工具,能夠檢測(cè)代碼片段中除了空格、注釋及換行外的內(nèi)容是否完全一致,且支持的語(yǔ)言包括:

          • Java
          • C#
          • C++
          • C
          • Objective-C
          • JavaScript (ECMAScript)
          • COBOL, ABAP
          • Ruby
          • Lisp
          • SQL
          • Visual Basic
          • Groovy
          • Swift

          (2)傳送門(mén)

          http://www.harukizaemon.com/simian/get_it_now.html

          (3)simian檢測(cè)結(jié)果示例

          代碼體積優(yōu)化

          1、代碼優(yōu)化小建議

          (1)時(shí)刻保持良好的編程習(xí)慣,去除重復(fù)或者不用的代碼,慎用第三方庫(kù),選用體積小的第三方SDK

          (2)盡量不要使用自動(dòng)生成的代碼的sdk 比如butterknife和viewbinding、databinding

          (3)減少ENUM的使用,避免使用枚舉 單個(gè)枚舉會(huì)使應(yīng)用的 classes.dex 文件增加大約 1.0 到 1.4KB 的大小 請(qǐng)考慮使用 @IntDef 注釋

          2、代碼混淆Proguard

          (1)作用

          混淆器的 作用 不僅僅是 保護(hù)代碼,它也有 精簡(jiǎn)編譯后程序大小 的作用,其 通過(guò)縮短變量和函數(shù)名以及丟失部分無(wú)用信息等方式,能使得應(yīng)用包體積減小。

          i 瘦身:它可以檢測(cè)并移除未使用到的類、方法、字段以及指令、冗余代碼,并能夠?qū)ψ止?jié)碼進(jìn)行深度優(yōu)化。最后,它還會(huì)將類中的字段、方法、類的名稱改成簡(jiǎn)短無(wú)意義的名字。

          ii 安全:增加代碼被反編譯的難度,一定程度上保證代碼的安全。

          (2)代碼混淆形式

          代碼混淆的形式主要有 三種,如下所示:

          i:將代碼中的各個(gè)元素,比如類、函數(shù)、變量的名字改變成無(wú)意義的名字。例如將 hasValue 轉(zhuǎn)換成單個(gè)的字母 a。這樣,反編譯閱讀的人就無(wú)法通過(guò)名字來(lái)猜測(cè)用途。

          ii:重寫(xiě) 代碼中的 部分邏輯,將它變成 功能上等價(jià),但是又 難以理解 的形式。比如它會(huì) 改變循環(huán)的指令、結(jié)構(gòu)體。

          iii:打亂代碼的格式,比如多加一些空格或刪除空格,或者將一行代碼寫(xiě)成多行,將多行代碼改成一行。

          (3)Proguard踩坑經(jīng)驗(yàn)

          i:如果項(xiàng)目首次混淆,可能需要全局掃描所有的類和包名,可以先全量keep,然后再逐包放開(kāi)混淆

          ii:EventBus的java、kotlin的onEvent的坑

          iii:沒(méi)有序列化的內(nèi)部屬性類也需要keep

          3、sdk優(yōu)化

          (1)sdk接入標(biāo)準(zhǔn)

          i:不要為了某個(gè)小功能就隨意引入sdk,可以考慮源碼接入

          ii:郵件通知審核sdk是否接入

          (2)選擇第三方 SDK 的時(shí)候,我們可以將包大小作為選擇的指標(biāo)之一,我們應(yīng)該 盡可能地選擇那些比較小的庫(kù)來(lái)實(shí)現(xiàn)相同的功能

          (3)不要選擇重復(fù)功能的sdk,如果有,可以考慮去掉其他的 Picasso、Glide、Fresco

          (4)某些庫(kù)支持部分功能分離,不需要引入整個(gè)包 比如 Fresco,它將圖片加載的各個(gè)功能,如 webp、gif 功能進(jìn)行了剝離,它們都處于單個(gè)的庫(kù)當(dāng)中

          4、刪除重復(fù)的代碼

          可以使用上面的pmd和simian工具掃描出重復(fù)的代碼

          5、刪除未使用的代碼

          可以使用上面的coverage插件來(lái)輔助統(tǒng)計(jì)出未使用的代碼

          6、dex壓縮

          (1)內(nèi)聯(lián)R Field

          通過(guò)內(nèi)聯(lián) R Field 來(lái)進(jìn)一步對(duì)代碼進(jìn)行瘦身,此外,它也解決了 R Field 過(guò)多導(dǎo)致 MultiDex 65536 的問(wèn)題。要想實(shí)現(xiàn)內(nèi)聯(lián) R Field,我們需要 通過(guò) Javassist 或者 ASM 字節(jié)碼工具在構(gòu)建流程中內(nèi)聯(lián) R Field

          實(shí)現(xiàn)原理:

          android 中的 R 文件,除了 styleable 類型外,所有字段都是 int 型變量/常量,且在運(yùn)行期間都不會(huì)改變。所以可以在編譯時(shí),記錄 R 中所有字段名稱及對(duì)應(yīng)值,然后利用 ASM 工具遍歷所有 Class,將除 R$styleable.class 以外的所有 R.class 刪除掉,并且在引用的地方替換成對(duì)應(yīng)的常量

          使用工具:ThinRPlugin(美麗說(shuō)團(tuán)隊(duì)開(kāi)源)

          插件部分實(shí)現(xiàn):

          (2)dex壓縮--XZ Utils

          i:XZ Utils 是具有高壓縮率的免費(fèi)通用數(shù)據(jù)壓縮軟件,它同 7-Zip 一樣,都是 LZMA Utils 的后繼產(chǎn)品,內(nèi)部使用了 LZMA/LZMA2 算法。LZMA 提供了高壓縮比和快速解壓縮,因此非常適合嵌入式應(yīng)用

          ii:缺點(diǎn) 壓縮 Dex 的方式,那么首次生成 ODEX 的時(shí)間可能就會(huì)超過(guò)1分鐘

          iii:傳送門(mén)

          https://tukaani.org/xz/

          7、多dex關(guān)聯(lián)優(yōu)化-ReDex

          (1)背景

          Dex 的方法數(shù)就會(huì)超過(guò)65536個(gè),因此,必須采用 mutildex 進(jìn)行分包,但是此時(shí)每一個(gè) Dex 可能會(huì)調(diào)用到其它 Dex 中的方法,這種 跨 Dex 調(diào)用的方式會(huì)造成許多冗余信息 (1)多余的 method id:跨 Dex 調(diào)用會(huì)導(dǎo)致當(dāng)前dex保留被調(diào)用dex中的方法id,這種冗余會(huì)導(dǎo)致每一個(gè)dex中可以存放的class變少,最終又會(huì)導(dǎo)致編譯出來(lái)的dex數(shù)量增多,而dex數(shù)據(jù)的增加又會(huì)進(jìn)一步加重這個(gè)問(wèn)題。(2)其它跨dex調(diào)用造成的信息冗余:除了需要多記錄被調(diào)用的method id之外,還需多記錄其所屬類和當(dāng)前方法的定義信息,這會(huì)造成 string_ids、type_ids、proto_ids 這幾部分信息的冗余。

          (2) ReDex方案

          為了減少跨 Dex 調(diào)用的情況,我們必須 盡量將有調(diào)用關(guān)系的類和方法分配到同一個(gè) Dex 中。但是各個(gè)類相互之間的調(diào)用關(guān)系是非常復(fù)雜的,所以很難做到最優(yōu)的情況。所幸的是,ReDex 的 CrossDexDefMinimizer 類分析了類之間的調(diào)用關(guān)系,并 使用了貪心算法去計(jì)算局部的最優(yōu)解(編譯效果和dex優(yōu)化效果之間的某一個(gè)平衡點(diǎn))。使用 "InterDexPass" 配置項(xiàng)可以把互相引用的類盡量放在同個(gè) Dex,增加類的 pre-verify,以此提升應(yīng)用的冷啟動(dòng)速度

          (3)ReDex的5個(gè)功能

          Interdex:類重排和文件重排、Dex 分包優(yōu)化。其中對(duì)于類重排和文件重排,Google 在 Android 8.0 的時(shí)候引入了 Dexlayout,它是一個(gè)用于分析 dex 文件,并根據(jù)配置文件對(duì)其進(jìn)行重新排序的庫(kù)。與 ReDex 類似,Dexlayout 通過(guò)將經(jīng)常一起訪問(wèn)的部分 dex 文件集中在一起,程序可以因改進(jìn)文件位置從而擁有更好的內(nèi)存訪問(wèn)模式,以節(jié)省 RAM 并縮短啟動(dòng)時(shí)間。不同于ReDex的是它使用了運(yùn)行時(shí)配置信息對(duì) Dex 文件的各個(gè)部分進(jìn)行重新排序。因此,只有在應(yīng)用運(yùn)行之后,并在系統(tǒng)空閑維護(hù)的時(shí)候才會(huì)將 dexlayout 集成到 dex2oat 的設(shè)備進(jìn)行編譯

          Oatmeal:直接生成 Odex 文件

          StripDebugInfo:去除 Dex 中的 Debug 信息

          源碼中 access-marking 模塊:刪除 Java access 方法

          源碼中 type-erasure 模塊:類型擦除。

          (4)傳送門(mén)

          https://fbredex.com/docs/installation

          資源體積優(yōu)化

          1、圖片格式優(yōu)化

          (1)如果能用VectorDrawable來(lái)表示的話優(yōu)先使用VectorDrawable,如果支持WebP則優(yōu)先用WebP,而PNG主要用在展示透明或者簡(jiǎn)單的圖片,而其它場(chǎng)景可以使用JPG格式。針對(duì)每種圖片格式也有各類的優(yōu)化手段和優(yōu)化工具。

          (2)使用矢量圖片

          可以使用矢量圖形來(lái)創(chuàng)建獨(dú)立于分辨率的圖標(biāo)和其他可伸縮圖片。使用矢量圖片能夠有效的減少App中圖片所占用的大小,矢量圖形在Android中表示為VectorDrawable對(duì)象。使用VectorDrawable對(duì)象,100字節(jié)的文件可以生成屏幕大小的清晰圖像,但系統(tǒng)渲染每個(gè)VectorDrawable對(duì)象需要大量的時(shí)間,較大的圖像需要更長(zhǎng)的時(shí)間才能出現(xiàn)在屏幕上。因此只有在顯示小圖像時(shí)才考慮使用矢量圖形。有關(guān)使用VectorDrawable的更多信息,請(qǐng)參閱 Working with Drawables。

          (3)使用WebP

          如果App的minSdkVersion高于14(Android 4.0+)的話,可以選用WebP格式,因?yàn)閃ebP在同畫(huà)質(zhì)下體積更?。╓ebP支持透明度,壓縮比比JPEG更高但顯示效果卻不輸于JPEG,官方評(píng)測(cè)quality參數(shù)等于75均衡最佳), 可以通過(guò)PNG到WebP轉(zhuǎn)換工具來(lái)進(jìn)行轉(zhuǎn)換。

          2、圖片壓縮

          (1)png格式圖片可以在tinyPng網(wǎng)站上壓縮,或者使用pngcrush、pngquant或zopflipng等工具壓縮

          而不會(huì)丟失圖像質(zhì)量。所有這些工具都可以減少PNG文件大小,同時(shí)保持圖像質(zhì)量。

          pngcrush工具特別有效:此工具在PNG過(guò)濾器和zlib(Deflate)參數(shù)上迭代,使用過(guò)濾器和參數(shù)的每個(gè)組合來(lái)壓縮圖像。然后選擇產(chǎn)生最小壓縮輸出的配置

          (2)JPEG文件,可以使用packJPG或guetzli等工具將JPEG文件壓縮的更小,這些工具能夠在保持圖片質(zhì)量不變的情況下,把圖片文件壓縮的更小。guetzli工具更是能夠在圖片質(zhì)量不變的情況下,將文件大小降低35%

          3、開(kāi)啟資源壓縮shrinkResources

          (1)Android的編譯工具鏈中提供了一款資源壓縮的工具,可以通過(guò)該工具來(lái)壓縮資源,如果要啟用資源壓縮,可以在build.gradle文件中將shrinkResources true

          (2)需要注意的是,Android構(gòu)建工具是通過(guò)ResourceUsageAnalyzer來(lái)檢查哪些資源是無(wú)用的,

          當(dāng)檢查到無(wú)用的資源時(shí)會(huì)把該資源替換成預(yù)定義的版本。主要是針對(duì) .png、.9.png、.xml 提供了 TINY_PNG、TINY_9PNG、TINY_XML 這 3 個(gè) byte 數(shù)組的預(yù)定義版本。資源壓縮工具默認(rèn)是采用 安全壓縮模式 來(lái)運(yùn)行,可以通過(guò)開(kāi)啟 嚴(yán)格壓縮模式 來(lái)達(dá)到 更好的瘦身效果

          4、語(yǔ)言資源優(yōu)化

          語(yǔ)言資源優(yōu)化 讓構(gòu)建工具移除指定語(yǔ)言之外的所有資源(可以刪除sdk里面的語(yǔ)言資源) resConfigs "zh", "zh-rCN"

          5、圖片分辨率優(yōu)化

          根據(jù)項(xiàng)目實(shí)際需要,大部分圖片可以只保留一套xxhdpi圖片

          6、屬性代碼替代shape xml

          (1)背景

          項(xiàng)目中為了滿足ui需求,使用了大量android shape來(lái)生成各式各樣的背景。這些背景大多數(shù)只有圓角,描邊,填充色等信息不一樣,但是種類繁多,無(wú)法兼容,需要我們使用大量的xml文件來(lái)生產(chǎn)多種多樣的背景,目前項(xiàng)目中多達(dá)數(shù)百個(gè)

          (2)解決思路

          i:自定義HllRoundBackground類來(lái)構(gòu)建一個(gè)android 原生GradientDrawable來(lái)表示背景shape

          ii:自定義屬性來(lái)表示常用的android shape屬性,包含圓角,填充色,描邊,根據(jù)狀態(tài)改變填充顏色,描邊顏色,字體顏色,以及漸變色屬性

          iii:繼承android原生LayoutInflater.Factory2類,用它來(lái)生成View,并檢查該View上是否有自定義屬性,如果有嘗試生成背景,并設(shè)置到該View上。把該Factory注入到系統(tǒng)中,必要的時(shí)候,由我們代替系統(tǒng)的LayoutInflater創(chuàng)建View

          (3)代碼設(shè)計(jì)方案

          image.png

          (4)使用示例

          <TextView
              android:layout_width="match_parent"
              android:layout_height="wrap_content"
              android:text="20元"
              app:hll_corners_radius = "6dp"
              app:hll_stroke_width = "1dp"
              app:hll_solid_normal_color = "@color/white"
              app:hll_solid_selected_color = "@color/color_0dff6600"
              app:hll_stroke_normal_color = "@color/gray_15_percent"
              app:hll_stroke_selected_color = "@color/color_ff6600"/>

          7、資源混淆-AndResGuard

          (1) AndResGuard方案

          直接處理apk. 不依賴源碼,不依賴編譯過(guò)程,僅僅輸入一個(gè)安裝包,得到一個(gè)混淆包

          image.png

          (2)AndResGuard處理流程

          i:resources.arsc:它記錄了資源文件的名稱與路徑,使用混淆后的短路徑 res/s/a,可以減少文件的大小。

          ii:metadata 簽名文件:簽名文件 MANIFEST.MF 與 CERT.SF 需要記錄所有文件的路徑以及它們的哈希值,使用短路徑可以減少這兩個(gè)文件的大小。

          iii:ZIP 文件:ZIP 文件格式里面通過(guò)其索引記錄了每個(gè)文件 Entry 的路徑、壓縮算法、CRC、文件大小等等信息。短路徑的優(yōu)化減少了記錄文件路徑的字符串大小

          AndResGuard工作流程圖

          image.png

          (3)傳送門(mén)

          https://github.com/shwenzhang/AndResGuard

          (4)與7z極限壓縮

          (5)AndResGuard混淆后的資源名

          image.png

          (6)AndResGuard踩坑經(jīng)驗(yàn)

          i:資源混淆之白名單

          代碼掃描調(diào)用getIdentifier()方法的地方

          ii:開(kāi)啟7zip壓縮之后會(huì)影響圖片加載速度,會(huì)對(duì)app啟動(dòng)速度有點(diǎn)影響

          8、刪除重復(fù)資源

          可以使用上面的ApkChecker工具掃描出apk中重復(fù)的資源

          9、刪除未使用資源

          (1)可以使用上面的ApkChecker工具掃描出apk中沒(méi)有使用的資源

          (2)也可以使用Android Studio自帶的lint插件掃描項(xiàng)目中沒(méi)有使用的資源

          Analyze -> Run Inspection by Name -> unused Resources

          其他包體積優(yōu)化方案

          1、資源動(dòng)態(tài)加載方案

          (1)原理

          把一些使用頻率相對(duì)低一些的資源不打包進(jìn)apk,需要的時(shí)候在下載到本地進(jìn)行使用(這些資源可能包括動(dòng)畫(huà)文件、字體文件、so庫(kù)、zip壓縮包等)

          (2)資源動(dòng)態(tài)加載架構(gòu)圖

          image.png

          (3)部分類UML設(shè)計(jì)

          image.png

          (4)資源動(dòng)態(tài)配置示例

          (5)動(dòng)態(tài)so加載

          i:正常so加載流程

          安裝app的時(shí)候,PMS會(huì)把指定架構(gòu)的so庫(kù),拷貝到 data/data/[包名]/lib 下面

          啟動(dòng)app的時(shí)候,會(huì)把系統(tǒng)的so文件夾,以及 安裝包的so文件夾位置 給 BaseDexClassLoader 中的屬性DexPathList 下面屬性的 nativeLibraryDirectories 和 systemNativeLibraryDirectories 兩個(gè)File集合

          調(diào)用及使用 調(diào)用:System.loadLibrary("xxx")

          ii:動(dòng)態(tài)加載so方案

          System.loadLibrary()和System.load()最后都會(huì)調(diào)用DexPathList 的 findLibrary(), 通過(guò) DexPathList 中的 nativeLibraryDirectories 和systemNativeLibraryDirectories兩個(gè)文件夾集合,生成一個(gè)NativeLibraryElement[],然后從這里面找對(duì)應(yīng)的so,返回全路徑

          hook了DexPathList 中的 nativeLibraryDirectories,在這個(gè)文件夾集合中又添加一個(gè)自定義的文件夾

          流程圖如下:

          image.png

          2、本地圖片轉(zhuǎn)網(wǎng)圖

          (1)原理

          編譯時(shí) (1)上傳圖片 (2)刪除圖片源文件 (3)保存鏈接信息

          運(yùn)行時(shí) (1)解析鏈接信息 (2)Hook Android Drawable圖片加載流程 (3)自定義Drawable,觸發(fā)網(wǎng)絡(luò)圖片下載,還原系統(tǒng)的Drawable圖片繪制流程

          (2)aapt流程圖

          (3)本地圖片轉(zhuǎn)網(wǎng)圖流程圖

          (4)編譯時(shí)刪除本地圖片

          (5)運(yùn)行時(shí)加載圖片

          3、字節(jié)碼優(yōu)化Bytex

          (1)Bytex簡(jiǎn)介

          ByteX字節(jié)跳動(dòng)團(tuán)隊(duì)開(kāi)發(fā)的一個(gè)基于gradle transform api和ASM的字節(jié)碼插件平臺(tái)(或許,你可以把它當(dāng)成一個(gè)有無(wú)限個(gè)插頭的插座?)

          目前集成了若干個(gè)字節(jié)碼插件,每個(gè)插件完全獨(dú)立,既可以脫離ByteX這個(gè)宿主而獨(dú)立存在,又可以自動(dòng)集成到宿主和其它插件一起整合為一個(gè)單獨(dú)的Transform。插件和插件之間,宿主和插件之間的代碼是完全解耦的(有點(diǎn)像組件化),這使得ByteX在代碼上擁有很好的可拓展性,新插件的開(kāi)發(fā)將會(huì)變得更加簡(jiǎn)單高效

          (2)傳送門(mén)

          ByteX/README_zh.md at master · bytedance/ByteX

          (3)字節(jié)碼優(yōu)化功能

          i:優(yōu)化多余賦值指令 (field-assign-opt-plugin)

          編譯期間去除代碼中不必要或者重復(fù)的賦值(默認(rèn)值)代碼,在虛擬機(jī)實(shí)例化時(shí)分配的內(nèi)存中默認(rèn)會(huì)給予默認(rèn)值,所以代碼中的默認(rèn)值是多余的,如下:

          private boolean aBoolean = false;
          private byte aByte = 0;
          private short aShort = 0;
          private char aChar = '\u0000';
          private int anInt = 0;
          private float aFloat = 0f;
          private double aDouble = 0d;
          private long aLong = 0l;

          ii:刪除某些方法調(diào)用 (method-call-opt-plugin)

          比如我們的調(diào)試日志Log.d()只是在開(kāi)發(fā)調(diào)試階段使用,發(fā)布包完全不需要此類代碼

          iii:常量?jī)?nèi)聯(lián)(const-inline-plugin)

          編譯期間內(nèi)聯(lián)并優(yōu)化掉項(xiàng)目中的編譯期間常量字段,插件將對(duì)編譯期常量的運(yùn)算(對(duì)應(yīng)GETFIELD指令)進(jìn)行內(nèi)聯(lián)操作(對(duì)應(yīng)LDC指令),然后將對(duì)應(yīng)的字段進(jìn)行刪除優(yōu)化。插件會(huì)對(duì)可能的反射的代碼進(jìn)行分析,對(duì)于直接使用反射方式獲取運(yùn)行時(shí)常量字段進(jìn)行忽略優(yōu)化處理。

          4、so包瘦身

          (1)移除多余的so架構(gòu)

          defaultConfig {     
               ndk {         
                  abiFilters "armeabi"    
               } 

          i:一般應(yīng)用都不需要用到 neon 指令集,我們只需留下 armeabi 目錄就可以了。因?yàn)?armeabi 目錄下的 So 可以兼容別的平臺(tái)上的 So

          ii:缺點(diǎn):別的平臺(tái)使用時(shí)性能上就會(huì)有所損耗,失去了對(duì)特定平臺(tái)的優(yōu)化

          (2)移除調(diào)試符號(hào)

          使用 Android NDK 中提供的 arm-eabi-strip 工具從原生庫(kù)中移除不必要的調(diào)試符號(hào)

          5、Buck-刪除 Native Library 中無(wú)用的導(dǎo)出 symbol

          (1)Buck作用

          分析代碼中的 JNI 方法以及不同 Library 庫(kù)的方法調(diào)用,然后找出無(wú)用的 symbol 并刪除,這樣 Linker 在編譯的時(shí)候也會(huì)把 symbol 對(duì)應(yīng)的無(wú)用代碼給刪除。在 Buck 有 NativeRelinker 這個(gè)類,它就實(shí)現(xiàn)了這個(gè)功能,其 類似于 Native Library 的 ProGuard Shrinking 功能

          (2)使用

          刪除 Native Library 中無(wú)用的導(dǎo)出 symbol 使用facebook的Buck庫(kù),Buck有 NativeRelinker 這個(gè)類,可以刪除 Native Library 中無(wú)用的導(dǎo)出 symbol,其 類似于 Native Library 的 ProGuard Shrinking 功能。

          (3)傳送門(mén)

          https://github.com/facebook/buck

          插件化

          1、DL 動(dòng)態(tài)加載框架 ( 2014 年底)

          基于代理的方式實(shí)現(xiàn)插件框架,當(dāng)啟動(dòng)插件組件時(shí),首先啟動(dòng)一個(gè)代理組件,然后通過(guò)這個(gè)代理組件來(lái)構(gòu)建,啟動(dòng)插件組件

          支持的功能

          (1)plugin無(wú)需安裝即可由宿主調(diào)起。

          (2)支持用R訪問(wèn)plugin資源

          (3)plugin支持Activity和FragmentActivity(未來(lái)還將支持其他組件)

          (4)基本無(wú)反射調(diào)用

          (5)插件安裝后仍可獨(dú)立運(yùn)行從而便于調(diào)試

          (6)支持3種plugin對(duì)host的調(diào)用模式:

          無(wú)調(diào)用(但仍然可以用反射調(diào)用)。

          部分調(diào)用,host可公開(kāi)部分接口供plugin調(diào)用。這前兩種模式適用于plugin開(kāi)發(fā)者無(wú)法獲得host代碼的情況。

          完全調(diào)用,plugin可以完全調(diào)用host內(nèi)容。這種模式適用于plugin開(kāi)發(fā)者能獲得host代碼的情況。

          (7)只需引入DL的一個(gè)jar包即可高效開(kāi)發(fā)插件,DL的工作過(guò)程對(duì)開(kāi)發(fā)者完全透明

          傳送門(mén):

          https://github.com/singwhatiwanna/dynamic-load-apk

          2、DroidPlugin ( 2015 年 8 月)

          360 手機(jī)助手實(shí)現(xiàn)的一種插件化框架,它可以直接運(yùn)行第三方的獨(dú)立 APK 文件,完全不需要對(duì) APK 進(jìn)行修改或安裝。一種新的插件機(jī)制,一種免安裝的運(yùn)行機(jī)制,是一個(gè)沙箱

          功能:

          (1)插件APK完全不需做任何修改,可以獨(dú)立安裝運(yùn)行、也可以做插件運(yùn)行。要以插件模式運(yùn)行某個(gè)APK,你「無(wú)需」重新編譯、無(wú)需知道其源碼。

          (2)插件的四大組件完全不需要在Host程序中注冊(cè),支持Service、Activity、BroadcastReceiver、ContentProvider四大組件

          (3)插件之間、Host程序與插件之間會(huì)互相認(rèn)為對(duì)方已經(jīng)"安裝"在系統(tǒng)上了。

          (4)API低侵入性:極少的API。HOST程序只是需要一行代碼即可集成Droid Plugin

          (5)超強(qiáng)隔離:插件之間、插件與Host之間完全的代碼級(jí)別的隔離:不能互相調(diào)用對(duì)方的代碼。通訊只能使用Android系統(tǒng)級(jí)別的通訊方法。

          (6)支持所有系統(tǒng)API

          (7)資源完全隔離:插件之間、與Host之間實(shí)現(xiàn)了資源完全隔離,不會(huì)出現(xiàn)資源竄用的情況。

          (8)實(shí)現(xiàn)了進(jìn)程管理,插件的空進(jìn)程會(huì)被及時(shí)回收,占用內(nèi)存低。

          (9)插件的靜態(tài)廣播會(huì)被當(dāng)作動(dòng)態(tài)處理,如果插件沒(méi)有運(yùn)行(即沒(méi)有插件進(jìn)程運(yùn)行),其靜態(tài)廣播也永遠(yuǎn)不會(huì)被觸發(fā)

          缺點(diǎn):

          (1)無(wú)法在插件中發(fā)送具有自定義資源的Notification,例如:a. 帶自定義RemoteLayout的Notification b. 圖標(biāo)通過(guò)R.drawable.XXX指定的通知(插件系統(tǒng)會(huì)自動(dòng)將其轉(zhuǎn)化為Bitmap)

          (2)無(wú)法在插件中注冊(cè)一些具有特殊Intent Filter的Service、Activity、BroadcastReceiver、ContentProvider等組件以供Android系統(tǒng)、已經(jīng)安裝的其他APP調(diào)用。

          (3)缺乏對(duì)Native層的Hook,對(duì)某些帶native代碼的apk支持不好,可能無(wú)法運(yùn)行。比如一部分游戲無(wú)法當(dāng)作插件運(yùn)行。

          傳送門(mén):

          https://github.com/DroidPluginTeam/DroidPlugin

          3、Small ( 2015 年底)

          實(shí)現(xiàn)原理:(1)動(dòng)態(tài)加載類(2)資源分段(3)動(dòng)態(tài)代理注冊(cè)

          傳送門(mén):

          https://github.com/wequick/Small/wiki/Android

          4、VirtualAPK (2017年 6 月)

          VirtualAPK 是滴滴開(kāi)源的一套插件化框架,支持幾乎所有的 Android 特性,四大組件方面

          VirtualAPK架構(gòu)圖

          image.png

          傳送門(mén):

          https://github.com/didi/VirtualAPK/blob/master/README.md

          5、RePlugin (2017 年 7 月) RePlugin是一套完整的、穩(wěn)定的、適合全面使用的,占坑類插件化方案,由360手機(jī)衛(wèi)士的RePlugin Team研發(fā),也是業(yè)內(nèi)首個(gè)提出”全面插件化“(全面特性、全面兼容、全面使用)的方案

          RePlugin架構(gòu)圖

          image.png

          優(yōu)點(diǎn):

          • 「極其靈活」:主程序無(wú)需升級(jí)(無(wú)需在Manifest中預(yù)埋組件),即可支持新增的四大組件,甚至全新的插件
          • 「非常穩(wěn)定」:Hook點(diǎn)「僅有一處(ClassLoader),無(wú)任何Binder Hook」!如此可做到其「崩潰率僅為“萬(wàn)分之一”,并完美兼容市面上近乎所有的Android ROM」
          • 「特性豐富」:支持近乎所有在“單品”開(kāi)發(fā)時(shí)的特性。「包括靜態(tài)Receiver、Task-Affinity坑位、自定義Theme、進(jìn)程坑位、AppCompat、DataBinding等」
          • 「易于集成」:無(wú)論插件還是主程序,「只需“數(shù)行”就能完成接入」
          • 「管理成熟」:擁有成熟穩(wěn)定的“插件管理方案”,支持插件安裝、升級(jí)、卸載、版本管理,甚至包括進(jìn)程通訊、協(xié)議版本、安全校驗(yàn)等
          • 「數(shù)億支撐」:有360手機(jī)衛(wèi)士龐大的「數(shù)億」用戶做支撐,「三年多的殘酷驗(yàn)證」,確保App用到的方案是最穩(wěn)定、最適合使用的

          傳送門(mén):

          https://github.com/Qihoo360/RePlugin/blob/dev/README_CN.md

          6、Shadow

          騰訊自主研發(fā)的Android插件框架,經(jīng)過(guò)線上億級(jí)用戶量檢驗(yàn),號(hào)稱“零hook”

          Shadow主要具有以下特點(diǎn):

          (1)復(fù)用獨(dú)立安裝App的源碼:插件App的源碼原本就是可以正常安裝運(yùn)行的。

          (2)零反射無(wú)Hack實(shí)現(xiàn)插件技術(shù):從理論上就已經(jīng)確定無(wú)需對(duì)任何系統(tǒng)做兼容開(kāi)發(fā),更無(wú)任何隱藏API調(diào)用和Google限制非公開(kāi)SDK接口訪問(wèn)的策略完全不沖突。

          (3)全動(dòng)態(tài)插件框架:一次性實(shí)現(xiàn)完美的插件框架很難,但Shadow將這些實(shí)現(xiàn)全部動(dòng)態(tài)化起來(lái),使插件框架的代碼成為了插件的一部分。插件的迭代不再受宿主打包了舊版本插件框架所限制。

          (4)宿主增量極?。旱靡嬗谌珓?dòng)態(tài)實(shí)現(xiàn),真正合入宿主程序的代碼量極小(15KB,160方法數(shù)左右)。(5)Kotlin實(shí)現(xiàn):core.loader,core.transform核心代碼完全用Kotlin實(shí)現(xiàn),代碼簡(jiǎn)潔易維護(hù)

          傳送門(mén)

          https://github.com/Tencent/Shadow

          總結(jié)

          以上是我們目前在Apk包體積優(yōu)化方面做的一些嘗試和積累,可以根據(jù)自身情況取舍使用

          通過(guò)上述優(yōu)化措施,貨拉拉32位包體積從82.69M減少到了33.86M,減少了60%

          由于自身業(yè)務(wù)特點(diǎn),我們暫時(shí)沒(méi)有使用插件化框架;

          最后,保持好的開(kāi)發(fā)習(xí)慣,砍掉不必要的功能才是保證包體積持續(xù)優(yōu)化的超級(jí)大招






          為了失聯(lián),歡迎關(guān)注我防備的小號(hào)



           

                                                   微信改了推送機(jī)制,真愛(ài)請(qǐng)星標(biāo)本公號(hào)??

          瀏覽 37
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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>
                  殴美肏屄视频免费看 | 国产精品女人久久久久 | 韩国一区二区在线观看 | 黑人大屌孟操日本女人 | 欧美美女被操 |