Android包优化个人总结

Android包优化个人总结

前言

打包apkapp运行本身会有很多用处,减少代码量,减少资源量,减少资源名等,都可以减少apk的大小。如果apk足够大,会发现,release包比debug包启动都会快很多。

所以优化非常有必要,同时为了安全,还需要数据加密和apk加固

优化项

基本配置

release {
    // 删除无用资源
    shrinkResources true
    // 开启混淆
    minifyEnabled true
    // apk对齐
    zipAlignEnabled true
    // 指定混淆规则文件
    proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    // 只使用中文资源,xhdpi图片资源
    resConfigs "zh", "xhdpi"
    // 配置主dex的规则,将首页和第二页需要的代码放在主dex中有利于提升app启动效率
    multiDexKeepProguard file("maindexlist.pro")
   // 配置签名
    signingConfig signingConfigs.release
    // 关闭所有debug
    jniDebuggable false
    renderscriptDebuggable false
    debuggable false
}

一、混淆

一些通用的配置

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontskipnonpubliclibraryclassmembers
-dontpreverify
-verbose
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-keepattributes *Annotation*,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable

1. 通用混淆配置

  1. 不混淆Android组件类,View类等需要在配置中使用的类
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-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 com.android.vending.licensing.ILicensingService
  1. 不混淆通用的如Serializable, Parcelable子类
-keepclassmembers class * implements android.os.Parcelable {
    static ** CREATOR;
}
-keepclassmembers class * implements android.os.Parcelable {
    public static final android.os.Parcelable$Creator CREATOR;
}

-keepnames class * implements java.io.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();
}
  1. 枚举类
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}
  1. 含有native方法的类
-keepclasseswithmembernames,includedescriptorclasses class * {
    native <methods>;
}
-keepclasseswithmembernames class * {
    native <methods>;
}
  1. androidx.annotation.Keep标注的类、方法、域
# AndroidX
-keep class androidx.annotation.Keep
-keep @androidx.annotation.Keep class * {*;}
-keepclasseswithmembers class * {
    @androidx.annotation.Keep <methods>;
}
-keepclasseswithmembers class * {
    @androidx.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
    @androidx.annotation.Keep <init>(...);
}

# 非AndroidX
-keep class android.support.annotation.Keep
-keep @android.support.annotation.Keep class * {*;}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}
-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);
}
  1. R类中的所有域(后面可以使用AndResGuard进行处理)
-keep class **.R$* {
    public static <fields>;
}
-keep class **.R

2. 库相关的类

引用的库中需要保留一些代码,通常只需要去相对应的库文档中查找即可,下面举一些常用的库

  1. Glide
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep public class * extends com.bumptech.glide.module.AppGlideModule
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
  **[] $VALUES;
  public *;
}

# for DexGuard only
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
  1. EventBus
-keepclassmembers class * {
    @org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
 
# Only required if you use AsyncExecutor
-keepclassmembers class * extends org.greenrobot.eventbus.util.ThrowableFailureEvent {
    <init>(java.lang.Throwable);
}
  1. Okio
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*
  1. Okhttp
# JSR 305 annotations are for embedding nullability information.
-dontwarn javax.annotation.**

# A resource is loaded with a relative path so the package of this class must be preserved.
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase

# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
-dontwarn org.codehaus.mojo.animal_sniffer.*

# OkHttp platform used only on JVM and when Conscrypt dependency is available.
-dontwarn okhttp3.internal.platform.ConscryptPlatform
  1. Retrofit

除了下面官方的配置之外,因为在jsonmodel时使用了反射,所以不能混淆model类、域

# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
# EnclosingMethod is required to use InnerClasses.
-keepattributes Signature, InnerClasses, EnclosingMethod

# Retrofit does reflection on method and parameter annotations.
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations

# Retain service method parameters when optimizing.
-keepclassmembers,allowshrinking,allowobfuscation interface * {
    @retrofit2.http.* <methods>;
}

# Ignore annotation used for build tooling.
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement

# Ignore JSR 305 annotations for embedding nullability information.
-dontwarn javax.annotation.**

# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
-dontwarn kotlin.Unit

# Top-level functions that can only be used by Kotlin.
-dontwarn retrofit2.KotlinExtensions
-dontwarn retrofit2.KotlinExtensions$*

# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>

二、减少资源名

AndResGuard是一个帮助你缩小APK大小的工具,他的原理类似Java Proguard,但是只针对资源。他会将原本冗长的资源路径变短,例如将res/drawable/wechat变为r/d/a。

当项目逐渐增大,包里面的资源越来越多,名字也越来越长(名字清晰当然易于阅读),AndResGuard的作用就越大,当使用AndResGuard后,会发现apk明显的缩短,并且某种程度上,进行了混淆。

官方地址:AndResGuard

1. 配置

在项目build.gradle中添加插件

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

在主模块build.gradle中添加,其中注意一定要将需要保留的资源文件名加入白名单,另外如果使用了如友盟、融云等库时,因为在这些库中使用资源文件名获取资源,所以一定要添加白名单,不然会出现资源找不到的问题。(我更喜欢用LeanCloud

andResGuard {
    // mappingFile = file("./resource_mapping.txt")
    mappingFile = null
    use7zip = true
    useSign = true
    // 打开这个开关,会keep住所有资源的原始路径,只混淆资源的名字
    keepRoot = false
    // 设置这个值,会把arsc name列混淆成相同的名字,减少string常量池的大小
    fixedResName = "arg"
    // 打开这个开关会合并所有哈希值相同的资源,但请不要过度依赖这个功能去除去冗余资源
    mergeDuplicatedRes = true
    whiteList = [
        // for 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"
    ]
    compressFilePattern = [
        "*.png",
        "*.jpg",
        "*.jpeg",
        "*.gif",
    ]
    sevenzip {
         artifact = 'com.tencent.mm:SevenZip:1.2.17'
         //path = "/usr/local/bin/7za"
    }

    /**
    * 可选: 如果不设置则会默认覆盖assemble输出的apk
    **/
    // finalApkBackupPath = "${project.rootDir}/final.apk"

    /**
    * 可选: 指定v1签名时生成jar文件的摘要算法
    * 默认值为“SHA-1”
    **/
    // digestalg = "SHA-256"
}

2. 启动

使用Android Studio的同学可以再 andresguard 下找到相关的构建任务; 命令行可直接运行./gradlew resguard[BuildType | Flavor], 这里的任务命令规则和assemble一致。

详细使用查看官方地址AndResGuard

三、删除无用资源

虽然可以配置在打包时删除无用资源,但是Android Studio本身就提供找出未使用资源的方法

流程:Analyze -> Run Inspection by Name -> 输入Unused resources -> 选择需要查找的范围 -> OK

等待几分钟,就可以发现目前没有使用到的资源,根据自身情况删除,如果存在相互引用,可以多运行几次

如下图:

run inspection by name

unused resources

start unused resources

四、代码审查

这篇文章不讨论代码相关的事情,使用代码审查可以找出代码中的问题,可能引用了一些没有使用到的资源,同时建议开启阿里Java代码指导插件Alibaba Java Coding Guidelines,代码的质量就是我们程序员的脸面,当别人看你代码时,即使功能不强大,但是代码一定要漂亮。

使用方法:Analyze -> Inspect Code,运行结束可以在下方看到Android Studio帮忙找出的问题,虽然它没有那么智能,有些东西也不需要修改,但是我们可以做修改参考意见。

另外,还可以多看看Analyze下的一些功能,可以帮助提升代码质量。一经检查,发现修改根本停不下来…

一些可以参考的文章

Android APP终极瘦身指南

APK瘦身三步曲

Android 可能你想要的APK瘦身笔记

西瓜视频apk瘦身之 Java access 方法删除


   转载规则


《Android包优化个人总结》 Mycroft Wong 采用 知识共享署名 4.0 国际许可协议 进行许可。
 上一篇
StringBuilder、StringBuffer的线程安全到底是什么 StringBuilder、StringBuffer的线程安全到底是什么
StringBuilder、StringBuffer的线程安全到底是什么前言昨天看到这个面试题:String、StringBuilder、StringBuffer之间的区别是什么? 这个问题本身很简单: String是不可变的,对于字符串
下一篇 
Luban压缩实现分析 Luban压缩实现分析
Luban压缩实现分析前言Luban是一个非常好用的Android图片压缩库,据作者所言,作者逆向推算了微信的压缩算法,压缩结果很接近微信朋友圈压缩后的效果。在实际使用中,压缩的结果大大缩小了从相册选择出来的图片,质量也几乎没有太大差别。
  目录