Android 专题

腾讯开源,APK优化项目:Matrix ApkChecker

2019-01-07  本文已影响130人  tse1y

简介

Matrix是微信终端自研和正在使用的一套APM(Application Performance Management)系统。 Matrix-ApkChecker 作为Matrix系统的一部分,是针对android安装包的分析检测工具,根据一系列设定好的规则检测apk是否存在特定的问题,并输出较为详细的检测结果报告,用于分析排查问题以及版本追踪。Matrix-ApkChecker以一个jar包的形式提供使用,通过命令行执行 java -jar ApkChecker.jar 即可运行。

Matrix-ApkChecker 的使用

jar包下载地址 提取码: rtja

简单使用
java -jar E:/ApkChecker.jar --config E:/apk_config.json

将参数以json格式写在apk_config.json中,具体如下:
(具体使用只需要替换"--apk"、"--mappingTxt"、"--output"、"--rTxt"的内容)

{
  "--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"]
    }
  ]
}
具体使用 直接在命令行执行
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的命令行参数比较多,主要包括global参数和option参数两类:

  --apk   输入apk文件路径(默认文件名以apk结尾即可)
  --mappingTxt   代码混淆mapping文件路径 (默认文件名是mapping.txt)
  --resMappingTxt   资源混淆mapping文件路径(默认文件名是resguard-mapping.txt)
  --input   包含了上述输入文件的目录(给定--input之后,则可以省略上述输入文件参数,但上述输入文件必须使用默认文件名)
  --unzip   解压apk的输出目录
  --output   输出结果文件路径(不含后缀,会根据format决定输出文件的后缀)
  --format   结果文件的输出格式(例如 html、json等)
  --formatJar   实现了自定义结果文件输出格式的jar包
  --formatConfig   对结果文件输出格式的一些配置项(json数组格式)

global参数之后紧跟若干个Option,这些Option是可选的,一个Option表示针对apk的一个检测选项。

除了直接在命令行中带上详细参数外,也可以将参数配置以json的格式写到一个配置文件中,然后在命令行中使用

config CONFIG-FILE_PATH

指定配置文件的路径。一个典型的配置文件格式如下:

{
  "--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默认提供 html 、json、mm.html 以及 mm.json 四种输出格式。

功能

Matrix-ApkChecker 当前主要包含以下功能

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

列出超过一定大小的文件,可按文件后缀过滤,并且按文件大小排序

统计dex包含的方法数,并支持将输出结果按照类名(class)或者包名(package)来分组

检查apk是否经过了资源混淆,推荐使用资源混淆来进一步减小apk的大小

对于不含alpha通道的png文件,可以转成jpg格式来减少文件的大小

so文件的大小可能会在apk文件大小中占很大的比例,可以考虑在apk中只包含一个ABI版本的动态库

某个文件类型的所有文件都没有经过压缩,可以考虑是否需要压缩

编译之后,代码中对资源的引用都会优化成int常量,除了R.styleable之外,其他的R类其实都可以删除

对于两个内容完全相同的文件,应该去冗余

如果有多个动态库都依赖了STL,应该采用动态链接的方式而非多个动态库都去静态链接STL

apk中未经使用到的资源,应该予以删除

apk中未经使用的assets文件,应该予以删除

动态库经过裁剪之后,文件大小通常会减小很多

示例分析

下面,我们对一个示例apk使用Matrix-ApkChecker进行检查,并根据检查的结果进行针对性的减包优化。

从Matrix-ApkChecker的输出结果中可以看到示例apk的相关全局信息如下图所示:

global1.png 示例apk中包含的文件按类型统计如下图所示: file-type2.png

对于示例apk,我们使用Matrix-ApkChecker进行了全面检查,主要发现以下几个问题:

针对上述Matrix-ApkChecker检测出来的问题,做如下针对性的优化:

res/drawable-xxxhdpi 目下存在与 res/drawable 目录内容相同的文件,删除 res/drawable 目录下的 icon.png 以及 round.png。 删除之后,可以看到示例apk中png文件缩小了23.89 KB 。

ret-sub-duplicate7.png

从示例输出中可以看到,示例apk的 minSdkVersion 是18,android对于API >= 18的版本已经支持透明的webp。使用Android Studio自带的webp转换功能,选择无损压缩,将部分png文件(不含 .9.png )转成webp之后,示例apk的大小缩小了 7.03 KB

![ret-sub-unused-assets9.png](https://img.haomeiwen.com/i1901072/b1489cbb0f86f145.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

将assets/music目录下的 .mp3 文件删除,示例apk的大小缩减了 69.39 KB

ret-convert-file8.png

可以看到删除之后,apk中无用资源大大减少,同时示例apk中arsc文件大小缩减了 36.99 KB

ret-sub-unused-resource10.png

经过上述优化,示例apk的大小一共缩减了 137.3 KB 。

实现原理

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

1.输入的Apk文件首先会经过UnzipTask处理,解压到指定目录,在这一步还会做一些全局的准备工作,包括反混淆类名(读取mapping.txt)、反混淆资源(读取resMapping.txt)、统计文件大小等。
2.接下来的若干Task即用来实现各种检查规则,这些Task可以并行执行,下面一一简单介绍各个Task的实现方法:

实现方法:利用ApkTool中的 AXmlResourceParser 来解析二进制的AndroidManifest.xml文件,并且可以反混淆出AndroidManifest.xml中引用的资源名称。

实现方法:直接利用UnzipTask中统计的文件大小来过滤输出结果。

实现方法:利用google开源的 com.android.dexdeps 类库来读取dex文件,统计方法数。

实现方法:资源混淆之后的res文件夹会重命名成r,直接判断是否存在文件夹r即可判断是否经过了资源混淆。

实现方法:通过 java.awt.BufferedImage 类读取png文件并判断是否有alpha通道。

实现方法:直接判断lib文件夹下是否包含多个目录。

实现方法:通过nm工具来读取so的符号表,如果出现 std:: 即表示so静态链接了STL。

实现方法:同样是利用 com.android.dexdeps 类库来读取dex文件,找出R类以及field数目。

实现方法:直接利用UnzipTask中统计的各个文件的压缩前和压缩后的大小,判断压缩前和压缩后大小是否相等。

实现方法:通过比较文件的MD5是否相等来判断文件内容是否相同。

实现方法: (1)过读取R.txt获取apk中声明的所有资源得到declareResourceSet; (2)通过读取smali文件中引用资源的指令(包括通过reference和直接通过资源id引用资源)得出class中引用的资源classRefResourceSet; (3)通过ApkTool解析res目录下的xml文件、AndroidManifest.xml 以及 resource.arsc 得出资源之间的引用关系; (4)根据上述几步得到的中间数据即可确定出apk中未使用到的资源。

实现方法:搜索smali文件中引用字符串常量的指令,判断引用的字符串常量是否某个assets文件的名称

实现方法:使用nm工具读取动态库文件的符号表,若输出结果中包含no symbols字样则表示该动态库已经过裁剪

3.每个Task的输出结果保存在json对象中,然后通过 OutputFormater 来对输出结果进一步加工(可以转成html格式),也可以实现自己的OutputFormater自定义输出内容的格式。

Matrix-ApkChecker 的特点

微信在Jenkins上部署了Matrix-ApkChecker来检查编译产出的Apk,并将结果输出到APM系统中汇总分析。

前述所有的检查Task都是继承自ApkTask,开发者也可以通过继承ApkTask类来扩展实现自定义的检查规则。

Matrix-ApkChecker支持json格式和html格式的输出结果,默认的输出结果包含了最详尽的信息,开发者可以通过自定义输出结果的Formater来过滤精简输出信息。 只需要以下三步就可以实现自定义的输出结果格式: 1.继承TaskJsonResult或者TaskHtmlResult来精简自定义每个Task的输出信息 mmtaskjsonresult12.png
2.继承TaskResultRegistry并在其中注册自定义输出格式的名称和实现类 taskresultregistry13.png
3.将上述实现类打包成jar,并在Manifest文件中声明注册类的信息 taskresultregistry14.png

最后在使用Matrix-ApkChecker时通过--formatJar 参数指定自定义输出格式的jar包。

在微信终端APM系统中的应用

微信终端监控系统使用 Matrix-ApkChecker 来监测微信每个版本的apk大小变化,并针对每个版本提出优化issue和优化的suggesstion。

可以进一步查看某个版本apk的详情。下图显示了该版本各类型文件的占比情况: chart-pie17.png 对于该版本可能存在的问题,也会给出对应的suggesstion : chart-suggests18.png

原文链接

上一篇 下一篇

猜你喜欢

热点阅读