性能优化-APK瘦身

880 阅读6分钟

前言

Apk瘦身是一个高级开发需要掌握的高级技能。

APK结构

包含以下目录

  • 1.assets/:包含了应用的而资源,这些资源可以通过AssetManager对象获得。
  • 2.lib/: 包含针对处理器层面的被编译的代码。这个目录针对每个平台类型都有一个子目录,比如armebi,armebi-v7a,arm64-v8a,x86,x86_64和mips
  • 3.res/:包含了没被编译到resources.arsc的资源
  • 4.META-INF/:包含了CERT.SF 和CERT.RSA签名文件,也包含了MANIFEST.MF文件。(校验这个APK是否被人改动过) 包含以下文件
  • 4.classes.dex: 包含了能被Dalvik/Art虚拟机理解的dex文件格式的类
  • resources.arsc :包含了被编译的资源,该文件包含了res/values目录的所有配置的xml内容。打包工具讲xml内容编译成二进制形式并压缩,这些内容包含了语言字符串和style,还包含了哪些内容虽然不直接存储在resources.arsc文件中,但是给定了该内容的路径,比如布局文件和图片,所以又叫资源映射表。
  • AndroidManifest.xml:包含了主要的Android配置文件。这个文件列出了应用名称、版本、访问权限、引用的库文件。该文件使用二进制存储(还能看到应用的minSdkVersion targetSdkVersion等信息)。

启用资源缩减 (不打包)

如果在应用的 build.gradle 文件中启用了资源缩减: shrinkResources ,则 Gradle 在打包APK时可以自动忽略 未使用资源。 资源缩减只有在与代码缩减: minifyEnabled 配合使用时才能发挥作用。在代码缩减器移除所有不 使用的代码后,资源缩减器便可确定应用仍要使用的资源 。 image.png

使用lint分析器

该方式存在缺陷 会删掉通过代码调用getIndetify的资源 image.png image.png

动态库打包配置

so文件是由ndk编译出来的动态库,是 c/c++ 写的,所以不是跨平台的。ABI 是应用程序二进制接口简称 (Application Binary Interface),定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的 指令集,内存对齐到可用的系统函数库。在Android 系统中,每一个CPU架构对应一个ABI,目前支持的有: armeabi-v7a,arm64- v8a,x86,x86_64。目前市面上手机设备基本上都是arm架构, armeabi-v7a 几乎能兼容 所有设备。因此可以配置:

image.png 对于第三方服务,如百度地图、Bugly等会提供全平台的cpu架构。进行了上面的配置之后,表示只会把armeabiv7a打包进入Apk。从而减少APK大小。 对于arm64架构的设备,如果使用armeabi-v7a也能够兼容,但是不如使用arm64的so性能。因此现在部分应用市 场会根据设备提供不同架构的Apk安装。此时我们需要打包出针对arm64的apk与armv7a的apk,可以使用 productFlavor 。

image.png

image.png

1.减少资源图片大小(采用SVG)

Apk中图片应该算是占用空间最多的资源。我们可以使用webp减少png、jpg图片占用空间的大小。对于小图标也 可以使用矢量图。 矢量图可以创建与分辨率无关的图标和其他可伸缩媒体。使用这些图形可以极大地减少 APK 占用的空间。 矢量图 片在 Android 中以 VectorDrawable 对象的形式表示。借助 VectorDrawable 对象,100 字节的文件可以生成与 屏幕大小相同的清晰图片。 不过,系统渲染每个 VectorDrawable 对象需要花费大量时间,而较大的图片则需要更长的时间才能显示在屏幕 上。因此,建议仅在显示小图片时使用这些矢量图。

1.SVG导入

SVG(可缩放的矢量图),svg不会像位图一样因为缩放而让图片质量下降。优点在于可以减少apk的尺寸,常用于小图标。 svg是由xml定义的,标准的svg根节点为。Android中只支持,我们可以通过vector将svg的根节点转换成。
在androidstudio中打开工程,在res目录中点击右键

2.SVG批量导入

上述只是针对少量SVG图片的手动导入,如果有多个,可以考虑第三方工具svg2vector 进行批量转换。 执行转换命令: java -jar svg2vector-cli-1.0.0.jar -d . -o a -h 20 -w 20
-d 指定svg文件所在目录
-o 输出android vector 图像目录
-h 设置转换后svg的高
-w 设置转换后svg的宽

3.不支持SVG

如果SVG文件包含不受支持的功能,将在vector Asset Studio的底部显示一个错误提示。如

  • 1.滤镜效果:不支持投影、模糊和颜色矩阵等效果。
  • 2.文本:建议使用其他功夫剧将文本转化为形状。

矢量图向后兼容-生成PNG

Android 5.0(API21) 之前的版本不支持矢量图,使用vector Asset Studio有2种方式适配。
方式一:生成png格式的图片
vector Asset Studio 可在构建时 针对每种屏幕密度将矢量图转换成不同大小的位图,在build.gradle中配置如下: SVG适用于Gradle插件1.5及以上版本:

     defaultConfig {
        //5.0版本以下,将svg图片生成指定维度的png图片
        generatedDensities = ['xhdpi','xxhdpi']
    }

方式二:支持库(矢量图向后兼容)
在 build.gradle 中配置如下,适用于 Gradle 插件2.0及以上版本:

android{
    // Gradle Plugin 2.0+
    defaultConfig{
        // 利用支持库中的 VectorDrawableCompat 类,可实现 2.1 版本及更高版本中支持 VectorDrawable
        vectorDrawables.useSupportLibrary = true
    }
}
dependencies {
  // 支持库版本需要是 23.2 或更高版本
  compile 'com.android.support:appcompat-v7:23.2.0'
}

![image.png](https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f1a25519f6274b31a1b21865034fd68f~tplv-k3u1fbpfcp-watermark.image?)

使用矢量图 必须使用 app:srcCompat 属性,而不是 android:src,如下: 矢量图在android里面格式

矢量图实现图片选择器效果

减少国际化资源配置

因为默认下 即时我们没有适配配置各种语言的,但是系统还是会给我们进行了配置,所以我们还是需要手动去配置指定的资源配置

动态库打包配置

压缩代码

压缩资源

资源混淆(因为资源混淆后会将文件名改成a b c之类的字母 这样也能减少apk大小)

混淆步骤

public class Main {

    public static String OUTDIR = "resguard-tools/build/andres";
    public static String CONFIG = "resguard-tools/config";

    public static void main(String[] args) throws Exception {
        // 7z a -tzip xxx.apk src/* -mx=9 极限压缩
        // 修改apk中某个文件极限压缩为不压缩,如res/raw/a.mp3、assets/a.mp3等文件 不能压缩 否则无法播放
        // 7z a -tzip xxx.apk src/* -mx=0 不压缩

        //强制压缩列表 使用7z压缩apk
        List<String> forceCompress = readForceCompress(CONFIG + "/compressData.txt");

        //待处理APK
        File apkFile = new File("app/build/outputs/apk/debug/app-debug.apk");
        System.out.println(apkFile.getAbsolutePath());

        //混淆并压缩apk
        Obfuscater obfuscater = new Obfuscater(forceCompress, apkFile);
        obfuscater.obfuscate();

    }


    static List<String> readForceCompress(String config) throws Exception {
        ArrayList<String> compressData = new ArrayList<>();
        File compress = new File(config);
        FileInputStream fis = new FileInputStream(compress);
        BufferedReader reader = new BufferedReader(new InputStreamReader(fis));
        String line;
        while ((line = reader.readLine()) != null) {
            compressData.add(line);
        }
        reader.close();
        return compressData;
    }
}

混淆ARSC文件

其他!