• 瑞金千人雨伞拼图创世界吉尼斯纪录 2019-07-02
  • “熊孩子”与幼儿教育 2019-06-15
  • 港媒称中国手游在韩国吸金美日手游遭韩玩家冷落 2019-06-15
  • 酒与植物 轻酌一樽是花香 2019-05-13
  • 台军为帮农民每餐吃香蕉 台媒改善两岸关系是根本 2019-05-12
  • 原脉——大地肌体上的血脉文章中国国家地理网 2019-05-11
  • 用更加过硬的作风追求改革新突破 2019-05-11
  • 欧版T90暴露法德坦克硬伤 VT4我离最强只差两步 2019-05-10
  • 【专题】河北省无偿献血领导小组会议报道 2019-05-10
  • 科幻推理剧《端脑》北京首映 搜狐匠心“烧脑”之作获赞 2019-05-09
  • 【大家谈】激励实干担当,谱写奋斗“进行曲” 2019-05-08
  • 回复@老老保老张工:连马克思都要重构个人所有制也!你不会自以为比马克思还高明吧? 2019-05-08
  • 学习宣传贯彻习近平新时代中国特色社会主义思想系列研讨会br第三场研讨会发言摘编 2019-05-07
  • 2000年出生的孩子属龙,要风得风,要雨得雨 2019-05-07
  • 美国防部宣布暂停8月美韩联合军演 2019-05-06
  • 生肖时时彩开奖:神操作 之 「玲珑宝塔」优化 Apk 包大小 [复制链接]

    2019-11-11 16:32
    静心Study 阅读:249 评论:0 赞:0
    Tag:  优化Apk包大小

    香港生肖时时彩 www.eptu.com.cn 九分喜欢,一分尊严,放弃你,也放过自己,愿你安好,在多年以后不要记起深爱你的我。

    图片描述

    絮絮叨

    工作不长不短,之前未曾考虑过深处,只是停留写出来了,便是完美。

    而今的处境,不尴不尬,岁月刚好,背起行囊,继续前行。

    如今的 5 G 也在万众瞩目瞩目下翩翩起舞,而 Android 近些年也惹得不少争议,所谓的谣言,不过尔尔。

    每个人的追求不一样,尽自己最大努力吧。

    如何减少 Apk 大小,一直以来都是处于观望状态,懒得折腾,其实还是不会,Low 的一批。

    Today,一起来搞一波~

    欢迎各位指正~

    现学现卖~

    一脑图,览无余

    图片描述

    玲珑宝塔镇万物

    首先附上一张现在 Apk 大小图:
    在这里插入图片描述
    未做任何处理原包大小为 10 MB,加固之后将近 11 MB。

    以此为例,一起看看经过我们玲珑宝塔升级完,最终还剩下多少精华?

    图片描述

    一层镇妖魔(减少 4.1 MB)

    来到第一层,我们先来简单分析下是什么造成 Apk 包如此“庞大”?在这里插入图片描述
    上图可看到 lib 下兼容了全面的 CPU 架构,试想一下,假设未来的未来多了短视频、直播、地图导航等等(不接受杠精),这块的大小会不会成倍数的增长。
    在这里插入图片描述
    上图可看到默认支持了 89 种语言类型,目前的应用暂时未国际化,这块也可直接设置兼容中文即可,原谅我这个强迫症。

    占比排行榜依次为:源代码、资源文件、lib。

    我们先挑个软柿子玩玩。

    1.1 设置支持语言(减少 0.2 MB)

    关于这块,个人觉得虽然占比较小,但是用啥玩啥,用不到的直接干掉。

    在 build.gradle 中设置仅支持中文:

        defaultConfig {
            ...
            // 仅支持 中文
            resConfigs "zh"
        }

    这块主要是根据现有项目需求来定,中心思想只有一个,兼容哪儿个就设置哪儿个国家语言,其他的直接忽略。

    设置完之后打个包,看下有没有什么变化。
    在这里插入图片描述
    从上图中可以很清晰的看到,经过设置仅支持的国家语言后,包大小减少了 0.2 MB。随后我们看下资源映射文件中关于 string 中会有什么变化。
    在这里插入图片描述
    默认语言中设置为中文,且应用也只支持了中文,少了好多东西,爽得很~

    1.2 设置支持的 CPU 架构类型(减少 1.5 MB)

    话说这里的 lib 为何兼容了这么多的 CPU 架构类型???

    正好走到这里,关于这块的小知识再次重温下,瞅瞅 Google 为我们提供的解释:

    不同的 Android 手机使用不同的 CPU,而不同的 CPU 支持不同的指令集。CPU 与指令集的每种组合都有专属的应用二进制接口,即 ABI。ABI 可以非常精确地定义应用的机器代码在运行时如何与系统交互。您必须为应用要使用的每个 CPU 架构指定 ABI。

    貌似 Google 商店现在支持对应的架构模式分发对应的 Apk 包,这点爽的每个包只需要兼容一种就好了。But,ummm。

    目前而言,项目中使用到真正用到 So 库没几个,全部兼容太过于浪费,据说 arm 属于通用,那么这里同语言设置一样,仅支持 arm 即可。

        defaultConfig {
            ...
            ndk {
                // 设置支持的SO库架构
                abiFilters "armeabi"
            }
        }

    打包运行后,继续查看现在包大?。?br>在这里插入图片描述
    这块一直属于个心病,之前的项目光是 So 库就占了很大一部分空间,很湿蛋疼。

    1.3 开启压缩、混淆(减少 2.4 MB)

    根据 Google 官网解释,当我们使用 Android Gradle 3.4.0 或者更高版本时,默认会启用 R8 编译器进行压缩、混淆以及优化,主要项以及作用如下:

    • 代码优化: 通过检测并安全移除未使用的类、字段、方法和属性;

    • 资源压缩: 从应用中移除未使用的资源,此过程包含移除库依赖项中未使用的资源文件。此项常常和代码压缩配合使用;

    • 混淆: 缩短类和成员的名称,从而减小 Dex 文件大??;

    • 优化: 检查并重写代码,进一步减小 Dex 文件大小。例如,如果 R8 检测到从未采用过给定 if/else 语句的 else {} 分支,R8 便会移除 else {} 分支的代码。

    这里需要注意一下:

    • 默认情况下并不启用压缩、混淆和代码优化功能。 因为开启后会造成 Debug 模式下编译时间较久。

    关于混淆文件,这里需要正好学习一下。

    混淆的意义在于什么?(引入官方解释)

    • 混淆处理的目的是通过缩短应用的类、方法和字段的名称来减小应用的大小

    混淆效果(摘自官方):

    androidx.appcompat.app.ActionBarDrawerToggle$DelegateProvider -> a.a.a.b:
        androidx.appcompat.app.AlertController -> androidx.appcompat.app.AlertController:
            android.content.Context mContext -> a
            int mListItemLayout -> O
            int mViewSpacingRight -> l
            android.widget.Button mButtonNeutral -> w
            int mMultiChoiceItemLayout -> M
            boolean mShowTitle -> P
            int mViewSpacingLeft -> j
            int mButtonPanelSideLayout -> K

    混淆需注意:

    • Android 四大组件不能混淆;

    • 反射、注解、枚举不能混淆;

    • JS、Native 调用的方法不能混淆;

    • 基础 Bean 类以及序列化实体类不能混淆;

    • 自定义控件不能混淆;

    • 资源文件不能混淆(当然也有骚操作);

    随后列举常用混淆规则(语法):

    • 保留某个类

      -keep public class com.hlq.Love
    • 保留某包下的所有类及其内部类

      -keep class com.hlq.** {*;}
    • 不显示指定类警告
      dontwarn com.hlq.**

    具体规则可文末查看官方手册。

    接下来跟着官网一起实践一波~

        buildTypes {
            release {
                // 打开资源压缩
                shrinkResources true
                // 开启混淆操作
                minifyEnabled true 
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.config
            }
            debug {
                // 关闭资源压缩以及混淆操作
                shrinkResources false
                minifyEnabled false
                proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
                signingConfig signingConfigs.config
            }
        }

    这里需要注意,在 Debug 模式下需要关闭资源压缩以及混淆操作,否则会增加编译时间,一般在发布正式包时打开即可。

    这里附上现在项目使用的混淆文件,基于大芬儿提供混淆文件做了部分修改:

    #############################################
    #
    # 混淆基本指令
    #
    #############################################
    # 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
    -optimizationpasses 5
    
    # 混合时不使用大小写混合,混合后的类名为小写
    -dontusemixedcaseclassnames
    
    # 指定不去忽略非公共库的类
    -dontskipnonpubliclibraryclasses
    
    # 这句话能够使我们的项目混淆后产生映射文件
    # 包含有类名->混淆后类名的映射关系
    -verbose
    
    # 指定不去忽略非公共库的类成员
    -dontskipnonpubliclibraryclassmembers
    
    # 不做预校验,preverify是proguard的四个步骤之一,Android不需要preverify,去掉这一步能够加快混淆速度。
    -dontpreverify
    
    # 保留Annotation不混淆
    -keepattributes *Annotation*,InnerClasses
    
    # 避免混淆泛型
    -keepattributes Signature
    
    # 抛出异常时保留代码行号
    -keepattributes SourceFile,LineNumberTable
    
    # 指定混淆是采用的算法,后面的参数是一个过滤器
    # 这个过滤器是谷歌推荐的算法,一般不做更改
    -optimizations !code/simplification/cast,!field/*,!class/merging/*
    
    # 忽略警告
    -ignorewarnings
    
    #############################################
    #
    # 需要保留的公共部分
    #
    #############################################
    
    # 保留我们使用的四大组件,自定义的 Application 等等这些类不被混淆
    # 因为这些子类都有可能被外部调用
    -keep public class * extends android.app.Activity
    -keep public class * extends android.app.Appliction
    -keep public class * extends android.app.Service
    -keep public class * extends android.content.BroadcastReceiver
    -keep public class * extends android.content.ContentProvider
    -keep public class * extends android.app.backup.BackupAgentHelper
    -keep public class * extends android.preference.Preference
    -keep public class * extends android.view.View
    -keep public class com.android.vending.licensing.ILicensingService
    
    # 保留support下的所有类及其内部类
    -keep class android.support.** {*;}
    
    # 保留继承的
    -keep public class * extends android.support.v4.**
    -keep public class * extends android.support.v7.**
    -keep public class * extends android.support.annotation.**
    
    # 保留R下面的资源
    -keep class **.R$* {*;}
    
    # 保留本地native方法不被混淆
    -keepclasseswithmembernames class * {
        native <methods>;
    }
    
    # 保留在Activity中的方法参数是view的方法,保障layout中写的onClick不会被影响
    -keepclassmembers class * extends android.app.Activity{
        public void *(android.view.View);
    }
    
    # 保留枚举类不被混淆
    -keepclassmembers enum * {
        public static **[] values();
        public static ** valueOf(java.lang.String);
    }
    
    # 保留我们自定义控件(继承自View)不被混淆
    -keep public class * extends android.view.View{
        *** get*();
        void set*(***);
        public <init>(android.content.Context);
        public <init>(android.content.Context, android.util.AttributeSet);
        public <init>(android.content.Context, android.util.AttributeSet, int);
    }
    
    # 保留Parcelable序列化类不被混淆
    -keep class * implements android.os.Parcelable {
        public static final android.os.Parcelable$Creator *;
    }
    
    # 保留Serializable序列化的类不被混淆
    -keepclassmembers class * implements java.io.Serializable {
        static final long serialVersionUID;
        private static final java.io.ObjectStreamField[] serialPersistentFields;
        !static !transient <fields>;
        !private <fields>;
        !private <methods>;
        private void writeObject(java.io.ObjectOutputStream);
        private void readObject(java.io.ObjectInputStream);
        java.lang.Object writeReplace();
        java.lang.Object readResolve();
    }
    
    # 对于带有回调函数的onXXEvent、**On*Listener的,不能被混淆
    -keepclassmembers class * {
        void *(**On*Event);
        void *(**On*Listener);
    }
    
    # webView处理,项目中没有使用到webView忽略即可
    -keepclassmembers class fqcn.of.javascript.interface.for.webview {
        public *;
    }
    -keepclassmembers class * extends android.webkit.webViewClient {
        public void *(android.webkit.WebView, java.lang.String, android.graphics.Bitmap);
        public boolean *(android.webkit.WebView, java.lang.String);
    }
    -keepclassmembers class * extends android.webkit.webViewClient {
        public void *(android.webkit.webView, jav.lang.String);
    }
    
    # 移除Log类打印各个等级日志的代码,打正式包的时候可以做为禁log使用,这里可以作为禁止log打印的功能使用
    # 记得proguard-android.txt中一定不要加-dontoptimize才起作用
    # 另外的一种实现方案是通过BuildConfig.DEBUG的变量来控制
    -assumenosideeffects class android.util.Log {
        public static int v(...);
        public static int i(...);
        public static int w(...);
        public static int d(...);
        public static int e(...);
    }
    
    #############################################
    #
    # 处理项目中我们的部分
    #
    #############################################
    
    #-----------处理实体类---------------
    # 在开发的时候我们可以将所有的实体类放在一个包内,这样我们写一次混淆就行了
    -keep public class 实体类.**{*;}
    
    # Js 交互
    -keepclassmembers class JS 交互类地址{
      public *;
    }
    
    -keepattributes *JavascriptInterface*
    
    #############################################
    #
    # 处理第三方依赖库部分
    #
    #############################################
    
    # 此处按照实际项目中使用去官方查找对应的混淆代码块

    随后我们继续打包,查看混淆、资源压缩后 Apk 大小以及部分变化:
    在这里插入图片描述
    dex 从 3 个降低到 2 个。未 Keep 的文件均已混淆,而 Keep 的文件依旧傲娇挺立,如下图:
    在这里插入图片描述
    混淆操作,在一定程度增大了破解的难度。当然,也没有绝对的安全。

    R8 每次运行时都会创建一个 mapping.txt 文件,其中列出了混淆过的类、方法和字段名称与原始名称的映射关系。此映射文件还包含用于将行号映射回原始源文件行号的信息。R8 将此文件保存在 <module- name>/build/outputs/mapping/<build-type>/ 目录中。
    在这里插入图片描述
    线上版本肯定要进行混淆,那么针对线上版本报出的异常,我们又该如何处理呢?毕竟关键内容都变成无意义字符,鉴名其意不存在了。
    在这里插入图片描述
    iTerm 2 打开:
    在这里插入图片描述
    点击 ReTrace:
    在这里插入图片描述
    这块步骤如下:

    • 导入 Mapping 文件

    • 将混淆后错误日志拷贝黏贴到 Obfuscated stack trace 中

    • 点击右下角的 ReTrace!

    1.4 开启 Zipalign 优化

    这块我看的很湿懵逼,估计唯有鸡大行云流水了。简单摘自官方解释:

    zipalign 是一种归档对齐工具,可对 Android 应用文件进行重要的优化。其目的是要确保所有未压缩数据的开头均相对于文件开头部分执行特定的对齐。具体来说,它会使 APK 中的所有未压缩数据(例如图片或原始文件)在 4 字节边界上对齐。这样一来,即可使用 mmap() 直接访问所有部分,即使其中包含具有对齐限制的二进制数据也没关系。这样做的好处是可以减少运行应用时消耗的 RAM 容量。

    如何使用?很是 easy~

        buildTypes {
            release {
                // 开启Zipalign 优化
                zipAlignEnabled true
            }
            debug {
                zipAlignEnabled false
            }
        }

    看一下结果:
    在这里插入图片描述
    貌似没啥用哦,能加还是加上吧。

    二层镇仙神(减小 1.5 MB)

    来到第二层,我们再来开下资源映射文件中关于图片这块:
    在这里插入图片描述
    其实对于图片而言,真的是个很蛋疼的操作,不过还好,一些简单的小背景、小效果,现在大部分都直接采用 shape、selector 等实现,多少也避免引入了一些图片。

    对于图片优化,主要分为以下几点:

    • 套图的优化 -SVG

    • 套图的优化 - Thit 着色器的应用

    • webp 的使用

    2.1 套图的优化 - SVG

    什么是套图呢?

    好比应用中的某个 Icon,一般来讲,UI 都会为我们提供 n 套图,以便于我们适配不同分辨率记性,大概的目录如下:
    在这里插入图片描述
    例如下面的这些大大小小的 Icon,一个个拷贝、改名也是比较痛苦的:
    在这里插入图片描述
    这个时候,SVG 便派上了用场。

    可缩放矢量图形(英語:Scalable Vector Graphics,SVG)是一种基于可扩展标记语言(XML),用于描述二维矢量图形的图形格式。 SVG由W3C制定,是一个开放标准。

    SVG 优势:

    • 节约空间、内存

    SVG 劣势:

    • 不支持透明度以及渐变

    ummm,需要注明一点,Android 6.0 + 支持,6.0 以下需要做兼容处理。不过现在应该也没必要兼容那么低的版本了吧?

    如何在 Android Studio 中创建一张 SVG 图片呢?如下所示:
    在这里插入图片描述
    弹出如下界面,在此页面可以选择直接导入 Android 内置 Icon 库图,还是手动加载 SVG or PSD 格式,看需选择。
    在这里插入图片描述
    放个操作图省事儿点:
    在这里插入图片描述
    使用也很 easy:

        <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/iv_toolbar_back"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:scaleType="fitXY"
                app:srcCompat="@drawable/ic_arrow_back" />

    别忘记在 build 里面对此设置:

        defaultConfig {
            ...
            // 强制 Gradle 在编译时不自动生成兼容低版本的位图资源
            vectorDrawables.useSupportLibrary = true
            // 生成指定类型的图片资源
            vectorDrawables.generatedDensities = ['xhdpi', 'xxhdpi', 'xxxhdpi'] 
        }

    建议直接下载 svg,导入 svg,这个真心比较爽。

    推荐下良心企业,阿里的 iconfont:

    经过一通折腾,阿里下载 SVG,之后 Android Studio 导入 SVG,巴拉巴拉,好歹改完了,哈哈哈

    2.2 套图的优化 - Tint 着色器

    先来举个天马行空的例子,例如同一张图片在不同的状态下显示不同,比如成功绿色,失败红色等等。按照以往的习惯,那肯定要求至少提供每种状态对应的图片,不然我怎么搞?

    但是有个实实在在的问题,那就是,图片一样,只是颜色发生了变化,假设五种状态,我们就需要引入至少五张图片,那么,可否只需要一张图片,针对不同的状态,我们渲染不同的颜色呢?

    当然可以,这就是今天要说到的 Tint 着色器。有了它,最简洁明了一点,至少能帮我剩下很多可谓是“无用”图片,大大节省了很多空间,我们的 Apk 更加“干练”。

    再举一个我们项目中常见的例子,首页 Tab 栏,如下图:
    在这里插入图片描述
    Tab 切换,字体变色、图片变色,这个见怪不怪了吧。上来至少给我提供八张图,四张默认,四张选中,然后通过 selector 文件设置,不给图没法做。对吧,这就是之前最实际的想法,嗯,还感觉自己可 dei 了。

    一起先来回顾下以前的 low 写法:

    Step 1:至少提供八张图后,设置 icon 引用 selector 文件:

    <?xml version="1.0" encoding="utf-8"?>
    <menu xmlns:android="//schemas.android.com/apk/res/android">
        <item
            android:id="@+id/nav_home"
            android:icon="@drawable/selector_menu_home"
            android:title="@string/nav_home" />
        ...
    </menu>

    Step 2:定义 Selector:

    <?xml version="1.0" encoding="utf-8"?>
    <selector xmlns:android="//schemas.android.com/apk/res/android">
        <item android:state_checked="true" android:drawable="@drawable/ic_tab_home_sel"/>
        <item android:drawable="@drawable/ic_tab_home"/>
    </selector>

    这样写固然木有问题,但是平白无故多了很多图片,在今天的我来看,必须不能忍,今天只用四张图,干他~

    这里吐槽下,由于之前底部导航采用 BottomNavigationView 方式,折腾好半天踩折腾出来,中间无数次想放弃了。但回头一想,我好歹也是跟随我鸡大的,况且抽烟的时候还要和文哥交流呢。艾玛,不容易,容小弟我抽根烟,ummm,没烟了


    我来说两句
    您需要登录后才可以评论 登录 | 立即注册
    facelist
    所有评论(0)
    领先的中文移动开发者社区
    18620764416
    7*24全天服务
    意见反?。[email protected]

    扫一扫关注我们

    Powered by Discuz! X3.2© 2001-2019 Comsenz Inc.( 香港生肖时时彩 )

  • 瑞金千人雨伞拼图创世界吉尼斯纪录 2019-07-02
  • “熊孩子”与幼儿教育 2019-06-15
  • 港媒称中国手游在韩国吸金美日手游遭韩玩家冷落 2019-06-15
  • 酒与植物 轻酌一樽是花香 2019-05-13
  • 台军为帮农民每餐吃香蕉 台媒改善两岸关系是根本 2019-05-12
  • 原脉——大地肌体上的血脉文章中国国家地理网 2019-05-11
  • 用更加过硬的作风追求改革新突破 2019-05-11
  • 欧版T90暴露法德坦克硬伤 VT4我离最强只差两步 2019-05-10
  • 【专题】河北省无偿献血领导小组会议报道 2019-05-10
  • 科幻推理剧《端脑》北京首映 搜狐匠心“烧脑”之作获赞 2019-05-09
  • 【大家谈】激励实干担当,谱写奋斗“进行曲” 2019-05-08
  • 回复@老老保老张工:连马克思都要重构个人所有制也!你不会自以为比马克思还高明吧? 2019-05-08
  • 学习宣传贯彻习近平新时代中国特色社会主义思想系列研讨会br第三场研讨会发言摘编 2019-05-07
  • 2000年出生的孩子属龙,要风得风,要雨得雨 2019-05-07
  • 美国防部宣布暂停8月美韩联合军演 2019-05-06
  • 农村淘宝代购怎么做赚钱 亚运会排球比分 养羊是繁殖赚钱还是育肥赚钱 福彩25选7开奖信息 百人牛牛偷分技巧 财神捕鱼在哪里下载 今天黑龙江22选5开奖 吉林11选5中奖新闻 排球比分去哪可以查 大富彩票游戏 南粤36选7更新开奖结果 老11选5图表投注 平码料 河北福彩快三技巧 河南22选5尾数走势图表 网络棋牌频道节目表