騰訊開源,APK優(yōu)化項(xiàng)目:Matrix ApkChecker

簡介

Matrix是微信終端自研和正在使用的一套APM(Application Performance Management)系統(tǒng)。 Matrix-ApkChecker 作為Matrix系統(tǒng)的一部分,是針對android安裝包的分析檢測工具,根據(jù)一系列設(shè)定好的規(guī)則檢測apk是否存在特定的問題,并輸出較為詳細(xì)的檢測結(jié)果報(bào)告,用于分析排查問題以及版本追蹤。Matrix-ApkChecker以一個(gè)jar包的形式提供使用,通過命令行執(zhí)行 java -jar ApkChecker.jar 即可運(yùn)行。

Matrix-ApkChecker 的使用

jar包下載地址 提取碼: rtja

簡單使用
java -jar E:/ApkChecker.jar --config E:/apk_config.json

將參數(shù)以json格式寫在apk_config.json中,具體如下:
(具體使用只需要替換"--apk"、"--mappingTxt"、"--output"、"--rTxt"的內(nèi)容)

{
  "--apk": "E:/app_1.1.3_1812270_2019-01-08.apk",
  "--mappingTxt": "E:/android/project/app-1/app/build/outputs/mapping/release/mapping.txt",
  "--output": "E:/apk-checker-result",
  "--format": "mm.html,mm.json",
  "--formatConfig": [{
    "name": "-countMethod",
    "group": [{
      "name": "Android System",
      "package": "android"
    },
      {
        "name": "java system",
        "package": "java"
      },
      {
        "name": "com.tencent.test.$",
        "package": "com.tencent.test.$"
      }
    ]
  }],
  "options": [{
    "name": "-manifest"
  },
    {
      "name": "-fileSize",
      "--min": "5",
      "--order": "desc",
      "--suffix": "png, jpg, jpeg, gif, arsc"
    },
    {
      "name": "-countMethod",
      "--group": "package"
    },
    {
      "name": "-checkResProguard"
    },
    {
      "name": "-findNonAlphaPng",
      "--min": "5"
    },
    {
      "name": "-checkMultiLibrary"
    },
    {
      "name": "-uncompressedFile",
      "--suffix": "png, jpg, jpeg, gif, arsc"
    },
    {
      "name": "-countR"
    },
    {
      "name": "-duplicatedFile"
    },

    {
      "name": "-unusedResources",
      "--rTxt": "E:/android/project/app-1/app/build/intermediates/symbols/release/R.txt",
      "--ignoreResources": ["R.raw.*",
        "R.style.*",
        "R.attr.*",
        "R.id.*",
        "R.string.ignore_*"
      ]
    },
    {
      "name": "-unusedAssets",
      "--ignoreAssets": ["*.so"]
    }
  ]
}
具體使用 直接在命令行執(zhí)行
java -jar ApkChecker.jar

即可以查看Matrix-ApkChecker的使用說明 (注意:下面所說的路徑為完整路徑,非相對路徑

Usages: 
    --config CONFIG-FILE-PATH
or
    [--input INPUT-DIR-PATH] [--apk APK-FILE-PATH] [--unzip APK-UNZIP-PATH] [--mappingTxt MAPPING-FILE-PATH] [--resMappingTxt RESGUARD-MAPPING-FILE-PATH] [--output OUTPUT-PATH] [--format OUTPUT-FORMAT] [--formatJar OUTPUT-FORMAT-JAR] [--formatConfig OUTPUT-FORMAT-CONFIG (json-array format)] [Options]

Options:
-manifest
     Read package info from the AndroidManifest.xml.
-fileSize [--min DOWN-LIMIT-SIZE (KB)] [--order ORDER-BY ('asc'|'desc')] [--suffix FILTER-SUFFIX-LIST (split by ',')]
     Show files whose size exceed limit size in order.
-countMethod [--group GROUP-BY ('class'|'package')]
     Count methods in dex file, output results group by class name or package name.
-checkResProguard
     Check if the resguard was applied.
-findNonAlphaPng [--min DOWN-LIMIT-SIZE (KB)]
     Find out the non-alpha png-format files whose size exceed limit size in desc order.
-checkMultiLibrary
     Check if there are more than one library dir in the 'lib'.
-uncompressedFile [--suffix FILTER-SUFFIX-LIST (split by ',')]
     Show uncompressed file types.
-countR
     Count the R class.
-duplicatedFile
     Find out the duplicated resource files in desc order.
-checkMultiSTL  --toolnm TOOL-NM-PATH
     Check if there are more than one shared library statically linked the STL.
-unusedResources --rTxt R-TXT-FILE-PATH [--ignoreResources IGNORE-RESOURCES-LIST (split by ',')]
     Find out the unused resources.
-unusedAssets [--ignoreAssets IGNORE-ASSETS-LIST (split by ',')]
     Find out the unused assets file.
-unstrippedSo  --toolnm TOOL-NM-PATH
     Find out the unstripped shared library file.

Matrix-ApkChecker的命令行參數(shù)比較多,主要包括global參數(shù)和option參數(shù)兩類:

  • global
  --apk   輸入apk文件路徑(默認(rèn)文件名以apk結(jié)尾即可)
  --mappingTxt   代碼混淆mapping文件路徑 (默認(rèn)文件名是mapping.txt)
  --resMappingTxt   資源混淆mapping文件路徑(默認(rèn)文件名是resguard-mapping.txt)
  --input   包含了上述輸入文件的目錄(給定--input之后,則可以省略上述輸入文件參數(shù),但上述輸入文件必須使用默認(rèn)文件名)
  --unzip   解壓apk的輸出目錄
  --output   輸出結(jié)果文件路徑(不含后綴,會根據(jù)format決定輸出文件的后綴)
  --format   結(jié)果文件的輸出格式(例如 html、json等)
  --formatJar   實(shí)現(xiàn)了自定義結(jié)果文件輸出格式的jar包
  --formatConfig   對結(jié)果文件輸出格式的一些配置項(xiàng)(json數(shù)組格式)

global參數(shù)之后緊跟若干個(gè)Option,這些Option是可選的,一個(gè)Option表示針對apk的一個(gè)檢測選項(xiàng)。

  • option參數(shù)
    • manifest 從AndroidManifest.xml文件中讀取apk的全局信息,如packageName、versionCode等。

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

      --min   文件大小最小閾值,單位是KB
      --order   按照文件大小升序(asc)或者降序(desc)排列
      --suffix   按照文件后綴過濾,使用","作為多個(gè)文件后綴的分隔符
      
    • countMethod 統(tǒng)計(jì)方法數(shù)

      group   輸出結(jié)果按照類名(class)或者包名(package)來分組
      
      
    • checkResProguard 檢查是否經(jīng)過了資源混淆(AndResGuard)

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

      min   png文件大小最小閾值,單位是KB
      
      
    • checkMultiLibrary 檢查是否包含多個(gè)ABI版本的動態(tài)庫

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

      suffix   按照文件后綴過濾,使用","作為多個(gè)文件后綴的分隔符
      
      
    • countR 統(tǒng)計(jì)apk中包含的R類以及R類中的field count

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

    • checkMultiSTL 檢查是否有多個(gè)動態(tài)庫靜態(tài)鏈接了STL

      toolnm   nm工具的路徑
      
      
    • unusedResources 發(fā)現(xiàn)apk中包含的無用資源

      rTxt   R.txt文件的路徑(如果在全局參數(shù)中給定了--input,則可以省略)
      ignoreResources   需要忽略的資源,使用","作為多個(gè)資源名稱的分隔符
      
      
    • unusedAssets 發(fā)現(xiàn)apk中包含的無用assets文件

      ignoreAssets   需要忽略的assets文件,使用","作為多個(gè)文件的分隔符
      
      
    • unstrippedSo 發(fā)現(xiàn)apk中未經(jīng)裁剪的動態(tài)庫文件

      toolnm   nm工具的路徑
      
      

除了直接在命令行中帶上詳細(xì)參數(shù)外,也可以將參數(shù)配置以json的格式寫到一個(gè)配置文件中,然后在命令行中使用

config CONFIG-FILE_PATH

指定配置文件的路徑。一個(gè)典型的配置文件格式如下:

{
  "--apk":"/Users/williamjin/SampleApplication/app/build/outputs/apk/release/AndResGuard_app-release-unsigned/app-release-unsigned_unsigned.apk",
  "--mappingTxt":"/Users/williamjin/SampleApplication/app/build/outputs/mapping/release/mapping.txt",
  "--resMappingTxt":"/Users/williamjin/SampleApplication/app/build/outputs/apk/release/AndResGuard_app-release-unsigned/resource_mapping_app-release-unsigned.txt",
  "--output":"/Users/williamjin/SampleApplication/app/build/outputs/apk-checker-result",
  "--format":"mm.html,mm.json",
  "--formatConfig":
  [
    {
      "name":"-countMethod",
      "group":
      [
        {
          "name":"Android System",
          "package":"android"
        },
        {
          "name":"java system",
          "package":"java"
        },
        {
          "name":"com.tencent.test.$",
          "package":"com.tencent.test.$"
        }
      ]
    }
  ],
  "options": [
    {
      "name":"-manifest"
    },
    {
      "name":"-fileSize",
      "--min":"10",
      "--order":"desc",
      "--suffix":"png, jpg, jpeg, gif, arsc"
    },
    {
      "name":"-countMethod",
      "--group":"package"
    },
    {
      "name":"-checkResProguard"
    },
    {
      "name":"-findNonAlphaPng",
      "--min":"10"
    },
    {
      "name":"-checkMultiLibrary"
    },
    {
      "name":"-uncompressedFile",
      "--suffix":"png, jpg, jpeg, gif, arsc"
    },
    {
      "name":"-countR"
    },
    {
      "name":"-duplicatedFile"
    },
    {
      "name":"-checkMultiSTL",
      "--toolnm":"/Users/williamjin/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-nm"
    },
    {
      "name":"-unusedResources",
      "--rTxt":"/Users/williamjin/SampleApplication/app/build/intermediates/symbols/release/R.txt",
      "--ignoreResources"
      :["R.raw.*",
        "R.style.*",
        "R.attr.*",
        "R.id.*",
        "R.string.ignore_*"
      ]
    },
    {
      "name":"-unusedAssets",
      "--ignoreAssets":["*.so" ]
    },
    {
      "name":"-unstrippedSo",
      "--toolnm":"/Users/williamjin/Library/Android/sdk/ndk-bundle/toolchains/arm-linux-androideabi-4.9/prebuilt/darwin-x86_64/bin/arm-linux-androideabi-nm"
    }
  ]
}

其中,mm.html 和 mm.json 是微信使用的自定義輸出格式,Matrix-ApkChecker默認(rèn)提供 html 、json、mm.html 以及 mm.json 四種輸出格式。

功能

Matrix-ApkChecker 當(dāng)前主要包含以下功能

  • 讀取manifest的信息

從AndroidManifest.xml文件中讀取apk的全局信息,如packageName、versionCode等。

  • 按文件大小排序列出apk中包含的文件

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

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

統(tǒng)計(jì)dex包含的方法數(shù),并支持將輸出結(jié)果按照類名(class)或者包名(package)來分組

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

檢查apk是否經(jīng)過了資源混淆,推薦使用資源混淆來進(jìn)一步減小apk的大小

  • 搜索不含alpha通道的png文件

對于不含alpha通道的png文件,可以轉(zhuǎn)成jpg格式來減少文件的大小

  • 檢查是否包含多個(gè)ABI版本的動態(tài)庫

so文件的大小可能會在apk文件大小中占很大的比例,可以考慮在apk中只包含一個(gè)ABI版本的動態(tài)庫

  • 搜索未經(jīng)壓縮的文件類型

某個(gè)文件類型的所有文件都沒有經(jīng)過壓縮,可以考慮是否需要壓縮

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

編譯之后,代碼中對資源的引用都會優(yōu)化成int常量,除了R.styleable之外,其他的R類其實(shí)都可以刪除

  • 搜索冗余的文件

對于兩個(gè)內(nèi)容完全相同的文件,應(yīng)該去冗余

  • 檢查是否有多個(gè)動態(tài)庫靜態(tài)鏈接了STL

如果有多個(gè)動態(tài)庫都依賴了STL,應(yīng)該采用動態(tài)鏈接的方式而非多個(gè)動態(tài)庫都去靜態(tài)鏈接STL

  • 搜索apk中包含的無用資源

apk中未經(jīng)使用到的資源,應(yīng)該予以刪除

  • 搜索apk中包含的無用assets文件

apk中未經(jīng)使用的assets文件,應(yīng)該予以刪除

  • 搜索apk中未經(jīng)裁剪的動態(tài)庫文件

動態(tài)庫經(jīng)過裁剪之后,文件大小通常會減小很多

示例分析

下面,我們對一個(gè)示例apk使用Matrix-ApkChecker進(jìn)行檢查,并根據(jù)檢查的結(jié)果進(jìn)行針對性的減包優(yōu)化。

從Matrix-ApkChecker的輸出結(jié)果中可以看到示例apk的相關(guān)全局信息如下圖所示:

global1.png

示例apk中包含的文件按類型統(tǒng)計(jì)如下圖所示:
file-type2.png

對于示例apk,我們使用Matrix-ApkChecker進(jìn)行了全面檢查,主要發(fā)現(xiàn)以下幾個(gè)問題:

  • png文件(不包括.9.png)未經(jīng)壓縮,可以考慮一定程度的壓縮
    uncompress_file3.png
  • 存在一些冗余的文件,文件內(nèi)容相同的文件應(yīng)該只保留一份
    duplicated_file4.png
  • 存在無用資源,包括未使用的系統(tǒng)support包中的資源、第三方資源包中的無用資源以及示例app定義的資源
    unused_resources5.png
  • 存在無用的assets資源,應(yīng)該刪除
    unused_assets6.png

針對上述Matrix-ApkChecker檢測出來的問題,做如下針對性的優(yōu)化:

  • 首先刪除冗余文件

res/drawable-xxxhdpi 目下存在與 res/drawable 目錄內(nèi)容相同的文件,刪除 res/drawable 目錄下的 icon.png 以及 round.png。 刪除之后,可以看到示例apk中png文件縮小了23.89 KB 。

ret-sub-duplicate7.png

  • 將png文件轉(zhuǎn)換成webp格式

從示例輸出中可以看到,示例apk的 minSdkVersion 是18,android對于API >= 18的版本已經(jīng)支持透明的webp。使用Android Studio自帶的webp轉(zhuǎn)換功能,選擇無損壓縮,將部分png文件(不含 .9.png )轉(zhuǎn)成webp之后,示例apk的大小縮小了 7.03 KB

![ret-sub-unused-assets9.png](https://upload-images.jianshu.io/upload_images/1901072-b1489cbb0f86f145.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  • 刪除無用的assets文件

將assets/music目錄下的 .mp3 文件刪除,示例apk的大小縮減了 69.39 KB

ret-convert-file8.png

  • 刪除無用資源

可以看到刪除之后,apk中無用資源大大減少,同時(shí)示例apk中arsc文件大小縮減了 36.99 KB

ret-sub-unused-resource10.png

經(jīng)過上述優(yōu)化,示例apk的大小一共縮減了 137.3 KB 。

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

首先來看下Matrix-ApkChecker的整體工作流程
total-work-flow11.png

1.輸入的Apk文件首先會經(jīng)過UnzipTask處理,解壓到指定目錄,在這一步還會做一些全局的準(zhǔn)備工作,包括反混淆類名(讀取mapping.txt)、反混淆資源(讀取resMapping.txt)、統(tǒng)計(jì)文件大小等。
2.接下來的若干Task即用來實(shí)現(xiàn)各種檢查規(guī)則,這些Task可以并行執(zhí)行,下面一一簡單介紹各個(gè)Task的實(shí)現(xiàn)方法:

  • ManifestAnalyzeTask 用于讀取AndroidManifest.xml中的信息,如:packageName、verisonCode、clientVersion等。

實(shí)現(xiàn)方法:利用ApkTool中的 AXmlResourceParser 來解析二進(jìn)制的AndroidManifest.xml文件,并且可以反混淆出AndroidManifest.xml中引用的資源名稱。

  • ShowFileSizeTask 根據(jù)文件大小以及文件后綴名來過濾出超過指定大小的文件,并按照升序或降序排列結(jié)果。

實(shí)現(xiàn)方法:直接利用UnzipTask中統(tǒng)計(jì)的文件大小來過濾輸出結(jié)果。

  • MethodCountTask 可以統(tǒng)計(jì)出各個(gè)Dex中的方法數(shù),并按照類名或者包名來分組輸出結(jié)果。

實(shí)現(xiàn)方法:利用google開源的 com.android.dexdeps 類庫來讀取dex文件,統(tǒng)計(jì)方法數(shù)。

  • ResProguardCheckTask 可以判斷apk是否經(jīng)過了資源混淆

實(shí)現(xiàn)方法:資源混淆之后的res文件夾會重命名成r,直接判斷是否存在文件夾r即可判斷是否經(jīng)過了資源混淆。

  • FindNonAlphaPngTask 可以檢測出apk中非透明的png文件

實(shí)現(xiàn)方法:通過 java.awt.BufferedImage 類讀取png文件并判斷是否有alpha通道。

  • MultiLibCheckTask 可以判斷apk中是否有針對多個(gè)ABI的so

實(shí)現(xiàn)方法:直接判斷l(xiāng)ib文件夾下是否包含多個(gè)目錄。

  • CheckMultiSTLTask 可以檢測apk中的so是否靜態(tài)鏈接STL

實(shí)現(xiàn)方法:通過nm工具來讀取so的符號表,如果出現(xiàn) std:: 即表示so靜態(tài)鏈接了STL。

  • CountRTask 可以統(tǒng)計(jì)R類以及R類的中的field數(shù)目

實(shí)現(xiàn)方法:同樣是利用 com.android.dexdeps 類庫來讀取dex文件,找出R類以及field數(shù)目。

  • UncompressedFileTask 可以檢測出未經(jīng)壓縮的文件類型

實(shí)現(xiàn)方法:直接利用UnzipTask中統(tǒng)計(jì)的各個(gè)文件的壓縮前和壓縮后的大小,判斷壓縮前和壓縮后大小是否相等。

  • DuplicatedFileTask 可以檢測出冗余的文件

實(shí)現(xiàn)方法:通過比較文件的MD5是否相等來判斷文件內(nèi)容是否相同。

  • UnusedResourceTask 可以檢測出apk中未使用的資源,對于getIdentifier獲取的資源可以加入白名單

實(shí)現(xiàn)方法: (1)過讀取R.txt獲取apk中聲明的所有資源得到declareResourceSet; (2)通過讀取smali文件中引用資源的指令(包括通過reference和直接通過資源id引用資源)得出class中引用的資源classRefResourceSet; (3)通過ApkTool解析res目錄下的xml文件、AndroidManifest.xml 以及 resource.arsc 得出資源之間的引用關(guān)系; (4)根據(jù)上述幾步得到的中間數(shù)據(jù)即可確定出apk中未使用到的資源。

  • UnusedAssetsTask 可以檢測出apk中未使用的assets文件

實(shí)現(xiàn)方法:搜索smali文件中引用字符串常量的指令,判斷引用的字符串常量是否某個(gè)assets文件的名稱

  • UnStrippedSoCheckTask 可以檢測出apk中未經(jīng)裁剪的動態(tài)庫文件

實(shí)現(xiàn)方法:使用nm工具讀取動態(tài)庫文件的符號表,若輸出結(jié)果中包含no symbols字樣則表示該動態(tài)庫已經(jīng)過裁剪

3.每個(gè)Task的輸出結(jié)果保存在json對象中,然后通過 OutputFormater 來對輸出結(jié)果進(jìn)一步加工(可以轉(zhuǎn)成html格式),也可以實(shí)現(xiàn)自己的OutputFormater自定義輸出內(nèi)容的格式。

Matrix-ApkChecker 的特點(diǎn)

  • 以可執(zhí)行jar的方式提供使用,便于應(yīng)用到持續(xù)集成系統(tǒng)中

微信在Jenkins上部署了Matrix-ApkChecker來檢查編譯產(chǎn)出的Apk,并將結(jié)果輸出到APM系統(tǒng)中匯總分析。

  • 可通過擴(kuò)展Task自定義更多的檢查規(guī)則

前述所有的檢查Task都是繼承自ApkTask,開發(fā)者也可以通過繼承ApkTask類來擴(kuò)展實(shí)現(xiàn)自定義的檢查規(guī)則。

  • 可自定義檢查的輸出結(jié)果格式,便于將檢查結(jié)果展示在UI

Matrix-ApkChecker支持json格式和html格式的輸出結(jié)果,默認(rèn)的輸出結(jié)果包含了最詳盡的信息,開發(fā)者可以通過自定義輸出結(jié)果的Formater來過濾精簡輸出信息。 只需要以下三步就可以實(shí)現(xiàn)自定義的輸出結(jié)果格式: 1.繼承TaskJsonResult或者TaskHtmlResult來精簡自定義每個(gè)Task的輸出信息
mmtaskjsonresult12.png

2.繼承TaskResultRegistry并在其中注冊自定義輸出格式的名稱和實(shí)現(xiàn)類
taskresultregistry13.png

3.將上述實(shí)現(xiàn)類打包成jar,并在Manifest文件中聲明注冊類的信息
taskresultregistry14.png

最后在使用Matrix-ApkChecker時(shí)通過--formatJar 參數(shù)指定自定義輸出格式的jar包。

在微信終端APM系統(tǒng)中的應(yīng)用

微信終端監(jiān)控系統(tǒng)使用 Matrix-ApkChecker 來監(jiān)測微信每個(gè)版本的apk大小變化,并針對每個(gè)版本提出優(yōu)化issue和優(yōu)化的suggesstion。

  • 版本追蹤 從下圖可以直觀看到微信多個(gè)版本的apk大小變化趨勢。
    chart-line15.png
  • 版本issue 針對每個(gè)版本提出可以優(yōu)化的issue,如下圖所示:
    chart-issue16.png
  • 版本詳情

可以進(jìn)一步查看某個(gè)版本apk的詳情。下圖顯示了該版本各類型文件的占比情況:
chart-pie17.png

對于該版本可能存在的問題,也會給出對應(yīng)的suggesstion :
chart-suggests18.png

原文鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容