APK瘦身

news2024/10/7 20:29:33

先看下APK打包流程:APK打包流程_贺兰猪的博客-CSDN博客

知道了APK打包流程后想要瘦身,其实无非就是把整个APK的一些文件进行一个瘦身。

看下apk的这个文件。包括class、资源,资源生成arsc(资源映射表),manifest清单,再就是meta。

对这些文件如果能缩小的话,就能达到缩小apk的目的

SVG导入

SVG(Scalable Vector Graphics),可缩放矢量图。SVG不会像位图一样因为缩放而让图片质量下降。优点在于可以减小APK的尺寸。常用于简单小图标(200dp*200dp以下,超过会损耗性能)。

svg是由xml定义的,标准svg根节点为<svg>。 Android中只支持<vector>,我们可以通过 vector 将svg的根节点<svg> 转换为<vector>。

不能直接把美工小姐姐的svg复制进drawable,要手动在Android Studio中打开工程,在res目录中点击右键创建vector,选择svg做一个这样的转换。

批量转换SVG

如果有多个svg需要转换为android的vector,则可以通过第三方工具 svg2vector 进行批量转换。

执行转换命令:

java -jar svg2vector-cli-1.0.1.jar -d . -o a -h 20 -w 20

-d 指定svg文件所在目录

-o输出android vector图像目录 -h 设置转换后svg的高 -w设置转换后svg的宽

可以优化倍图和不同颜色同样的图,用tint 属性设置你需要的颜色,也可以用颜色选择器,定义不同点击状态下的颜色。坑:编译(不是run)的时候会出现在每个倍图下面都生成了png,与期望不合。要进行适配

方式一:在gradle中设置生成的密度图

defaultConfig {
      //只生成xxhdpi
     generatedDensities = ['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'
}

使用矢量图 必须使用app:srcCompat属性,而不是android:src。

国际化资源配置

比如只配置英语

defaultConfig {
        // 只适配英语
        resConfigs 'en'
       
}

动态库打包配置

so文件是由ndk编译出来的动态库,是c/c++写的,所以不是跨平台的。即每一个平台需要使用对应的so库。

ABI 是应用程序二进制接口简称(Application Binary Interface),定义了二进制文件(尤其是.so文件)如何运行在相应的系统平台上,从使用的指令集,内存对齐到可用的系统函数库。

在Android系统上,每一个CPU架构对应一个ABl:armeabi,armeabi-v7a,arm64-v8a,x86,x86_64,mips,mips64.

现在我们一般只需要配置armeabi-v7a即可。

正常手机上运行支持armeabi-v7a/armeabi就行了,像微信也就只支持了armeabi-v7a,定制机可能要支持多个

defaultConfig {
       
        ndk {
            abiFilters "armeabi-v7a"
        }
    }

移除无用的资源

AS 给我们提供了一键移除所有无用的资源,如图。但是这种方式不建议使用,因为如果某资源仅存在动态获取资源id 的方式,那么这个资源会被认为没有使用过,从而会直接被删除。

动态获取方式:

getResources().getldentifier("name","defType",getPackageName());

 或者lint检查也是

代码压缩

minifyEnabled设置成true就好了。当应用中包含许多库依赖项,但只使用它们的一小部分功能时,代码压缩起到的效果是很好的。

minifyEnabled不仅压缩了代码也混淆了代码,不需要混淆的类需要单独配置。未使用的Java代码会删除,开启的话编译会比较慢,因为要去做资源检查,平时运行一般关掉,不然打包调试很慢。

buildTypes{
    release {
        minifyEnabled true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'
    }
}

在某些情况下, R8 编译器很难做出正确判断,因而可能会移除应用实际上需要的代码。下面列举了几个示例,说明它在什么情况下可能会错误地移除代码:

  • 当应用通过 Java 原生接口 (JNI) 调用方法时
  • 当您的应用在运行时查询代码时(如使用反射)

如需修复错误并强制 R8 保留某些代码,要在 ProGuard 规则文件中添加 -keep 代码行。例如:

-keep public class MyClass

或者,也可以为要保留的代码添加 @Keep 注解。在类上添加 @Keep 可按原样保留整个类。在方法或字段上添加该注释,将使该方法/字段(及其名称)以及类名称保持不变。请注意,只有在使用 AndroidX 注解库且添加 Android Gradle 插件随附的 ProGuard 规则文件时,此注解才可用。

资源压缩

shrinkResources true

buildTypes{
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'
    }
}

要和代码压缩结合使用。 why?

因为有些未使用的方法里面使用了某些资源,如果不做代码压缩,那么这个方法被保存,里面链接的资源也不会被清掉,资源压缩就删的不够干净。只有未使用的代码干掉,相应的资源才能清理更干净。

从编译结果来看,某个xml没被使用,使用资源压缩,文件还在,但是文件里面的内容会被清掉

资源文件有一个严格模式和非严格模式。默认情况下是非严格模式。非严格模式是怎样的?如果是动态加载xml文件,他会匹配文件名前缀,相当于匹配一个通配符,也就说只要是以设置的链接资源为前缀的xml,他都认为是在使用的,所以在APP里面其他有相同前缀的资源也不会被删除。那如何设置严格模式?

res-》raw-〉建立一个keep.xml,设置内容如下:

<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
    tools:shrinkMode="strict"></resources>

但是严格模式的缺点是跟上面移除无用资源的方式一样,动态引用的资源文件也会被删除 ,需要单独在keep.xml里面设置tools:discard=""

注意:资源压缩器目前不会移除values/文件夹中定义的资源(例如字符串、尺寸、样式和颜色)。这是因为 Android 资源打包工具不允许 Gradle插件为资源指定预定义版本。

启动图片优化

可以用webp,它是安卓它特制的一种格式,主要是用于颜色复杂的图片,转成webp清晰度不会变低但整体会变小。

也可以用jpg替代,png是有透明度的,如果图片是不需要透明度的话,那你可以用jpg去替换。那么这是一种形式,因为一个像素点你用png的话占的的位数要比jpg高。像你是用argb8888,一个像素占4字节RGB_565,一个像素占2字节。相比ARGB_8888将节省一半的内存开销。一张大图片往往省下几K或者几十k。

还可以进行图片压缩。Android 图片优化_贺兰猪的博客-CSDN博客

apk拆分

根据屏幕不同或者abi进行拆分。国内上架不支持,了解一下

splits {
        density {
            enable true
            reset()
            include "mdpi", "hdpi"
        }
        abi {
            enable true
            reset()
            include "x86", "mips"
        }

    }
android.applicationVariants.all { variant ->
        variant.outputs.each { output ->
            // Filter is null for universal APKs.
            def filter = output.getFilter(com.android.build.OutputFile.ABI)

            if (filter == null) {
                tasks.findAll {
                    it.name.startsWith(
                            "crashlyticsUploadDistribution${variant.name.capitalize()}")
                }.each {
                    it.doFirst {
                        ext.betaDistributionApkFilePath = output.outputFile.absolutePath
                    }
                }

                tasks.findAll {
                    it.name.startsWith(
                            "crashlyticsUploadSymbols${variant.name.capitalize()}")
                }.each {
                    it.doFirst {
                        ext.betaDistributionApkFilePath = output.outputFile.absolutePath
                    }
                }
            }
        }
    }

效果如下图: 

 

splits里面配置属性的具体含义可以看 

Android官方技术文档翻译——Apk 拆分机制

资源混淆

启用资源文件res混淆及压缩的好处:

  • 1、减少apk文件大小
  • 2,增加反编译和二次打包的难度,混淆之后的apk不能用apktool之类的工具直接反编译。当然只是增加了难度,毕竟没有绝对安全;
  • 3减少运行时内存占用,在运行时各个res的key都是以String形式加载到内存中的,混淆之后key变短了很多内存占用肯定会变少。

当然上述的这些优势是有在一些大体量的应用上才会比较明显,比较小的apk可能不太明显。

资源加载过程分析
在开发过程中我们通过aapt生成的R.java中的常量来使用资源,而在编译之后使用常量的地方都会被替换为常量的值。见下图。

 也就是说我们通过Resource使用一个int数值来查找使用资源。那么Resource是怎么通过int数值找到具体的资源呢?我们解压apk可以看到里面有个resources.arsc文件,这个文件也是由aapt生成,文件中保存着资源id和资源key的映射关系。Resource就是按照这个映射关系找到资源的。

接下来我们具体看看这个arsc文件的具体内容。我们以单包名的应用来讲。结构见下图

其中res string pool 是res的字符串池,里面包含了字符串(就是大家开发过程中在String.xml文件增加的)和资源文件路径(包括图片,xml文件,raw文件。其中的值类似这样res/drawable/icon.png)
type string pool 是程序中使用的资源分类名称,包括 attr,anim,drawable。。。注意这里只是分类。

spec string pool 这个比较关键,这个字符串池中是所有资源的key。没有分类和config。具体的值就是R.java中各个常量的名称。

在这个字符串池后面就是各个资源分类列表,attr,anim。。。。每个分类中又包括若干config,config就是指hdpi,xhdpi,zh_rCN , land这些,大家知道android资源config有18个纬度。每个config中就是具体的res资源列表。

举例来说一下一个资源的查找过程,比如当我们调用getDrawable(R.drawable.icon)时候,先找到drawable分类,再根据当前的系统config找到匹配的config表。然后根据id找到对应的res数据。drawable在arsc文件中是当作string类型保存的。res数据中有这个资源在res string pool池中的索引。根据这个索引可以在字符串池中找到这个字符串。这个字符串其实是一个文件的路径,比如:res/drawable-hdpi/icon.png,然后读取apk中这个文件。

资源混淆原理和实现
看上面的例子,我们注意到一般情况下查找资源的过程中不会用到资源名,也就是key。当然res数据中也有当前res的key在key string pool中的索引,但一般情况下用不到。根据这个原理我们可以在打包完成之后对key进行混淆。而且刚刚说到res数据中保存的都是字符串在string pool中的索引。所以我们混淆只需要修改res string pool和spec string pool两个字符串池。其他的数据不用修改。

我们混淆的流程大概是这样的:

  • 1,解压apk文件;
  • 2,读取arsc文件;
  • 3,混淆spec string pool;
  • 4根据上一步混淆结果混淆res string pool;
  • 5,生成新的arsc文件;
  • 6,输出mapping文件;
  • 7,根据3,4步结果,修改res目录下所有文件名;
  • 8,打包压缩(7zip)(压缩程度更高);
  • 9,签名;
  • 10.zipalign。

keep
再说key的问题,刚刚说一般情况下用不到res的key。但是开发中我们有时候会通过key来查找资源。也就是说需要注意,需要通过key来使用的资源的key不能混淆。

可选方案

目前的实施方案有微信资源混淆打包工具AndResGuard和美团方案
美团方案需要自己实现函数,下面看下微信方案AndResGuard

1.根目录build文件中,添加插件的依赖

dependencies {
       classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.21'
    }

2.在主module(默认为APP)目录下,创建res_guard.gradle配置文件,名称可自定义。官方使用文档上是直接写在build.gradle文件中,这样会使build文件内容过多且繁杂,故建议放在单独的gradle中。

apply plugin: 'AndResGuard'
andResGuard {
    // mappingFile = file("./resource_mapping.txt")
    mappingFile = null
    use7zip = true
    useSign = true
    // It will keep the origin path of your resources when it's true
    keepRoot = false
    // If set, name column in arsc those need to proguard will be kept to this value
    fixedResName = "arg"
    // It will merge the duplicated resources, but don't rely on this feature too much.
    // it's always better to remove duplicated resource from repo
    mergeDuplicatedRes = true
//需要将所有通过getIdentifier访问的资源放入白名单。
//各种第三方的也要加进来,看github 的md
    whiteList = [
        // your icon
        "R.drawable.icon",
        // for fabric
        "R.string.com.crashlytics.*",
        // for google-services
        "R.string.google_app_id",
        "R.string.gcm_defaultSenderId",
        "R.string.default_web_client_id",
        "R.string.ga_trackingId",
        "R.string.firebase_database_url",
        "R.string.google_api_key",
        "R.string.google_crash_reporting_api_key",
        "R.string.project_id",
    ]
    compressFilePattern = [
        "*.png",
        "*.jpg",
        "*.jpeg",
        "*.gif",
    ]
    sevenzip {
        artifact = 'com.tencent.mm:SevenZip:1.2.21'
        //path = "/usr/local/bin/7za"
    }

    /**
    * Optional: if finalApkBackupPath is null, AndResGuard will overwrite final apk
    * to the path which assemble[Task] write to
    **/
    // finalApkBackupPath = "${project.rootDir}/final.apk"

    /**
    * Optional: Specifies the name of the message digest algorithm to user when digesting the entries of JAR file
    * Only works in V1signing, default value is "SHA-1"
    **/
    // digestalg = "SHA-256"
}

3.在主module(APP)目录下的build.gradle中,添加使用

apply from: 'res_guard.gradle'

资源混淆核心是修改资源映射表,资源混淆后查错比较麻烦,会有一个mapping 去看映射关系

可以看到在gradle插件中有替换压缩工具(7zip替换apkbuilder )进行压缩,这样压缩率更高

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/415878.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

快排(非递归)及计数排序算法

都学了递归版的快速排序为何还要再学非递归实现&#xff1f;由于在递归过程中&#xff0c;如果数据量过大&#xff0c;那么实现时容易导致栈溢出&#xff0c;虽然代码没有问题&#xff0c;但是就是会崩&#xff0c;因此要将其改为非递归来实现 文章目录一、快速排序&#xff08…

如何使用Mac远程控制Windows电脑?

在你开始之前&#xff0c;设置您要远程处理的 Windows 计算机。 先安装 Microsoft Remote Desktop。 您可以在“应用程序”文件夹中检查它。 如果在个人计算机上安装&#xff0c;请转到 Apple App Store 并下载 Microsoft Remote Desktop。 如果在 TXST 计算机上安装&#xff0…

【C语言】递归解决经典题目(汉诺塔问题、青蛙跳台阶问题)

简单不先于复杂&#xff0c;而是在复杂之后。 目录 1. 汉诺塔问题 1.1 简介及思路 1.2 代码实现 2. 青蛙跳台阶问题 2.1 简介及思路 2.2 代码实现 1. 汉诺塔问题 1.1 简介及思路 汉诺塔问题是一种经典的递归问题&#xff0c;起源于印度传说中的塔 of Brahma。问题描…

手把手教你学习IEC104协议和编程实现 十 故障事件与复位进程

故障事件 目的 在IEC104普遍应用之前,据我了解多个协议,再综合自动化协议中,有这么一个概念叫“事故追忆”,意思是当变电站出现事故的时候,不但要记录事故的时间,还需记录事故前后模拟量的数据,从而能从一定程度上分析事故产生的原因,这个模拟量就是和今天讲解的故障…

silvaco 仿真BJT

本次实验为利用silvaco仿真BJT器件&#xff0c;分析不同p区厚度以及p区不同掺杂浓度研究其电流增益的变化。 一、器件要求 区域 掺杂方式 掺杂浓度或 峰值浓度&#xff08;/cm3&#xff09; 厚度&#xff08;um&#xff09; 宽度&#xff08;um&#xff09; N-漂移区 均匀…

微服务框架【笔记-Nacos注册中心】

接上篇&#xff0c;继续学习微服务框架中的Nacos注册中心。 Nacos注册中心 一、认识和安装Nacos 1.认识Nacos Nacos 是阿里巴巴的产品&#xff0c;现在是 SpringCloud 中的一个组件。相比 Eureka 功能更加丰富。 2.安装Nacos 下面给大家展示windows安装Nacos步骤&#xff1a;…

网络互联技术与实践教程(汪双硕、姚羽)——第四章 路由技术

第四章 路由技术 4.1 路由原理 路由是指通过相互连接的网络将数据从源地点转发到目标地点的过程。在路由过程中&#xff0c;数据通常会经过一个或多个中间节点&#xff0c;路由发生在网络层。路由包含两个主要的动作&#xff1a;确定最佳路径和通过网络传输信息&#xff0c;后…

刷题笔记【6】| 快速刷完67道剑指offer(Java版)

本文已收录于专栏&#x1f33b;《刷题笔记》文章目录前言&#x1f3a8; 1、包含min函数的栈题目描述思路&#xff08;双栈法&#xff09;&#x1f3a8; 2、栈的压入弹出序列题目描述思路&#xff08;辅助栈&#xff09;&#x1f3a8; 3、从上往下打印二叉树题目描述思路&#x…

chapter-4-数据库语句

以下课程来源于MOOC学习—原课程请见&#xff1a;数据库原理与应用 考研复习 概述 SQL发展 注&#xff1a;关键词是哪些功能&#xff0c;尤其第一个create alter drop是定义功能 1.SQL功能强大&#xff0c;实现了数据定义、数据操纵、数据控制等功能 2.SQL语言简洁&#xff…

【Java版oj】day26跳台阶扩展问题、快到碗里来

目录 一、跳台阶扩展问题 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 二、快到碗里来 &#xff08;1&#xff09;原题再现 &#xff08;2&#xff09;问题分析 &#xff08;3&#xff09;完整代码 一、跳台…

tomcat配置虚拟路径映射磁盘文件列表图片回显和放大功能实现springboot项目的文件虚拟路径映射

tomcat映射磁盘图片1.以E盘为例&#xff0c;在E盘创建目录testReceive2.配置tomcat虚拟路径映射e盘本地文件3.代码层面创建上传文件&#xff08;此处为图片&#xff09;工具类3.1&#xff08;校验图片格式、获取当前主机ip、上传图片至本机目的地&#xff0c;获取上传图片地址&…

javaWeb(HTTP、Tomcat、Servlet)

目录 HTTP Web 服务器 - Tomcat 简介 基本使用&#xff1a;下载、安装、卸载、启动、关闭、配置、部署项目 IDEA中创建 Maven Web项目​编辑 IDEA中使用 Tomcat Servlet 快速入门 Servlet 执行流程 Servlet 生命周期 Servlet 体系结构 Servlet urlPattern配置 XM…

【从零开始学Skynet】实战篇《球球大作战》(五):gateway代码设计(中)

1、编码和解码 我们来实现两个辅助方法str_unpack和str_pack&#xff0c;用于消息的解码和编码。 &#xff08;1&#xff09;str_unpack代码 local str_unpack function(msgstr)local msg {}while true dolocal arg, rest string.match( msgstr, "(.-),(.*)")if…

【web自动化测试】

文章目录web自动化测试第一章 web自动化入门1.什么是自动化&#xff1f;1.1 优点2.什么是自动化测试&#xff1f;2.1 自动化测试能解决什么问题&#xff1f;2.2 自动化相关知识2.2.1优点2.2.2 误区2.3 自动化测试分类3.什么是Web自动化测试&#xff1f;3.1 什么Web项目适合做自…

Flutter 了解 Element

一 Element 概念 这个玩意的概念。到底是什么 &#xff1f; 官方解释是在树中特定位置的实例。 二 继承关系 element 有 ComponentElement 和 RenderObjectElement 之分 1 ComponentElement class StatelessElement extends ComponentElement class StatefulElement extend…

计及调度经济性的光热电站储热容量配置方法

目录 1 主要内容 目标函数 光热电站能量传递过程 2 部分程序 3 程序结果 4 程序链接 1 主要内容 该程序复现《计及调度经济性的光热电站储热容量配置方法》模型&#xff0c;综合考虑火电机组发电成本、光热发电并网消纳的环境效益和运行维护成本、系统旋转备用成本等调度…

rk3568点亮LCD(RGB)

rk3568 Android11/12 调试 RGB 屏 RGB一般是指RGB色彩模型(RGB color model)&#xff0c;是工业界的一种颜色标准。RGB接口占用的资源较多&#xff0c;所以这个接口的LCD刷新率非常快&#xff0c;软件控制也比较简单。缺点是控制需要增加电路&#xff0c;软件初始化需要增加程…

【BOM浏览器对象模型】

BOM浏览器对象模型1 本节目标2 BOM概述3 window对象的常见事件3.1 窗口加载事件3.2 调整窗口大小事件4 定时器4.1 两种定时器4.2 setTimeout()定时器4.3 停止setTimeout()定时器4.4 setInterval()定时器4.5 停止setInterval()定时器4.6 this指向问题5 JS执行队列5.1 JS是单线程…

BUUCTF-.htaccess-sql.fuzz-D盾

第七周第一次 目录 WEB [MRCTF2020]你传你&#x1f40e;呢 ​编辑 [极客大挑战 2019]HardSQL Crypto 萌萌哒的八戒 传统知识古典密码 Misc 假如给我三天光明 后门查杀 WEB [MRCTF2020]你传你&#x1f40e;呢 文件上传 我们进行尝试 设置一个 1.jpg的一句话木马 G…

RabbitMQ之高级特性

文章目录一、消息确认机制&#x1f389;1.1 消息发送确认(生产者)&#x1f539;confirm 确认模式&#x1f539;return 回退模式&#x1f6a9;1.2 消息接收确认(消费者)&#x1f538;none 自动确认&#x1f538;auto 异常确认&#x1f538;manual 手动确认二、消费端限流 (prefe…