美文网首页Android 专题
腾讯开源,APK优化项目:Matrix ApkChecker

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

作者: tse1y | 来源:发表于2019-01-07 15:45 被阅读130次

    简介

    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参数两类:

    • global
      --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的一个检测选项。

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

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

        --min   文件大小最小阈值,单位是KB
        --order   按照文件大小升序(asc)或者降序(desc)排列
        --suffix   按照文件后缀过滤,使用","作为多个文件后缀的分隔符
        
      • countMethod 统计方法数

        group   输出结果按照类名(class)或者包名(package)来分组
        
        
      • checkResProguard 检查是否经过了资源混淆(AndResGuard)

      • findNonAlphaPng 发现不含alpha通道的png文件

        min   png文件大小最小阈值,单位是KB
        
        
      • checkMultiLibrary 检查是否包含多个ABI版本的动态库

      • uncompressedFile 发现未经压缩的文件类型(即该类型的所有文件都未经压缩)

        suffix   按照文件后缀过滤,使用","作为多个文件后缀的分隔符
        
        
      • countR 统计apk中包含的R类以及R类中的field count

      • duplicatedFile 发现冗余的文件,按照文件大小降序排序

      • checkMultiSTL 检查是否有多个动态库静态链接了STL

        toolnm   nm工具的路径
        
        
      • unusedResources 发现apk中包含的无用资源

        rTxt   R.txt文件的路径(如果在全局参数中给定了--input,则可以省略)
        ignoreResources   需要忽略的资源,使用","作为多个资源名称的分隔符
        
        
      • unusedAssets 发现apk中包含的无用assets文件

        ignoreAssets   需要忽略的assets文件,使用","作为多个文件的分隔符
        
        
      • unstrippedSo 发现apk中未经裁剪的动态库文件

        toolnm   nm工具的路径
        
        

    除了直接在命令行中带上详细参数外,也可以将参数配置以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 当前主要包含以下功能

    • 读取manifest的信息

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

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

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

    • 统计方法数

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

    • 检查是否经过了资源混淆(AndResGuard)

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

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

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

    • 检查是否包含多个ABI版本的动态库

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

    • 搜索未经压缩的文件类型

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

    • 统计apk中包含的R类以及R类中的field count

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

    • 搜索冗余的文件

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

    • 检查是否有多个动态库静态链接了STL

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

    • 搜索apk中包含的无用资源

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

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

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

    • 搜索apk中未经裁剪的动态库文件

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

    示例分析

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

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

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

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

    • png文件(不包括.9.png)未经压缩,可以考虑一定程度的压缩 uncompress_file3.png
    • 存在一些冗余的文件,文件内容相同的文件应该只保留一份 duplicated_file4.png
    • 存在无用资源,包括未使用的系统support包中的资源、第三方资源包中的无用资源以及示例app定义的资源 unused_resources5.png
    • 存在无用的assets资源,应该删除 unused_assets6.png

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

    • 首先删除冗余文件

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

    ret-sub-duplicate7.png
    • 将png文件转换成webp格式

    从示例输出中可以看到,示例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文件

    将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的实现方法:

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

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

    • ShowFileSizeTask 根据文件大小以及文件后缀名来过滤出超过指定大小的文件,并按照升序或降序排列结果。

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

    • MethodCountTask 可以统计出各个Dex中的方法数,并按照类名或者包名来分组输出结果。

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

    • ResProguardCheckTask 可以判断apk是否经过了资源混淆

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

    • FindNonAlphaPngTask 可以检测出apk中非透明的png文件

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

    • MultiLibCheckTask 可以判断apk中是否有针对多个ABI的so

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

    • CheckMultiSTLTask 可以检测apk中的so是否静态链接STL

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

    • CountRTask 可以统计R类以及R类的中的field数目

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

    • UncompressedFileTask 可以检测出未经压缩的文件类型

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

    • DuplicatedFileTask 可以检测出冗余的文件

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

    • UnusedResourceTask 可以检测出apk中未使用的资源,对于getIdentifier获取的资源可以加入白名单

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

    • UnusedAssetsTask 可以检测出apk中未使用的assets文件

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

    • UnStrippedSoCheckTask 可以检测出apk中未经裁剪的动态库文件

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

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

    Matrix-ApkChecker 的特点

    • 以可执行jar的方式提供使用,便于应用到持续集成系统中

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

    • 可通过扩展Task自定义更多的检查规则

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

    • 可自定义检查的输出结果格式,便于将检查结果展示在UI
    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-line15.png
    • 版本issue 针对每个版本提出可以优化的issue,如下图所示: chart-issue16.png
    • 版本详情
    可以进一步查看某个版本apk的详情。下图显示了该版本各类型文件的占比情况: chart-pie17.png 对于该版本可能存在的问题,也会给出对应的suggesstion : chart-suggests18.png

    原文链接

    相关文章

      网友评论

        本文标题:腾讯开源,APK优化项目:Matrix ApkChecker

        本文链接:https://www.haomeiwen.com/subject/zhmurqtx.html