极致缩小APK体积(不去除V4,V7包)

我们都知道,现在的安卓APK体积是越来越大,一方面是安卓的碎片化严重,为了适配各大厂商而新增很多业务逻辑,(然而并不可能这么大),另一方面是SDK的v4,v7包占了一些体积, 还有一方面就是我们开发者的原因了,很多开发者并未优化体积,匆匆交差.今天就说说我是如何优化体积的,欢迎大神指正,留给小盆友学习.

碎片化严重

这个呢? 我是没办法咯!

v4 v7包的占用

这两个包是SDK的包 , 有部分控件会用到, 总大小占2.9MB左右 经压缩打包后占1.2MB,如果项目中没有用到的话可以去除他们 ,免得占地方.

去除v4 ,v7
  1. 在bind.gradle里去除他们的compile .

  2. 去掉之后会报错 ,在Activity里继承Activity 消除错误

  3. 在style里去除对v7包的引用

就这样v4,v7包就去除了, 未用到v4 v7包的apk新建也就200KB左右.

开发途中优化过程

1.删除无用资源
  1. 使用studio检测无用资源

    用android lint中Unused Resource

    操作方法:

1.png 2.png 3.png 4.png

完成之后就可以看到你没用到的资源了

  1. 打包时开启删除无用资源

build.gradle中设置:

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

该设置会在打包时自动将未使用的资源排除掉。

2.多业务线资源去重

如果app中包含多个业务线难免会有不同名的资源重复,推荐一个扫描重复资源的工具: Duplicate Cleaner Pro

3.资源压缩处理

利用工具对图片和音频进行压缩推荐两个工具

图片压缩工具:

limitPNG

ImageOptim

音频压缩工具:

http://trac.ffmpeg.org/wiki/CompilationGuide/MacOSX

4.微信混淆工具

微信混淆工具可以提高apk安全性,也能大大的减少apk大小强烈推荐 以下是关于这个工具的介绍和使用书名 资源混淆方案之微信vs美团

注意:如果你有读取包中文件的操作不能用这个工具,因为把资源名混淆后无法找到对应文件

5. 7zip压缩

http://www.7-zip.org/download.html

  1. 将APK包解压到文件夹

  2. 全选所有文件,右键“添加到压缩包”

  3. “压缩格式”必须“zip”

  4. “压缩等级”选择“极限压缩”

  5. “压缩方法”必须“Deflate”(试了Deflate64,BZip2,LZMA,PPMd都无法正常安装)

  6. “单词大小”选择“256”

  7. 将后缀改为APK即可

原APK大小:1,039,193 B

不同“单词大小”压缩能力比对:

128:963,793 B(极限压缩默认)

192:963,787 B

256:963,785 B(推荐)

258:963,911 B

最佳参数256,能将原APK文件 再压小 73.64KB(7.25%)!

有大神特此开发了小工具, 对APK极限压缩并对齐的工具

开源地址

可执行文件

最新源代码

开源地址

说明:“运行”之后会在原APK所在文件夹生成两个APK,分别为:

1.原APK名字_NEW.APK:这是7zip重新进行极限压缩后的新APK文件,默认没有对齐(此APK可删除)

2.原APK名字_Aligned.APK:这是对NEW.APK进行对齐后产生的最终APK包文件。(最终发布此APK即可)

6 . 最后奉上我的bind.gradle日常配置 (调整后使用)

apply plugin: 'com.android.application'

static def releaseTime() {
    return new Date().format("yyyy-MM-dd", TimeZone.getTimeZone("UTC"))
}

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.1"
    defaultConfig {
        applicationId "com.townwang.town"
        resConfigs "zh","en"//仅支持中文和英文
        minSdkVersion 14
        targetSdkVersion 27
        versionCode 6
        versionName "2.1"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            //混淆
            minifyEnabled true
            shrinkResources true//打开资源压缩
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            zipAlignEnabled true //是否使用zipalign工具压缩
        }
        debug {
            //混淆
            minifyEnabled true
            shrinkResources true//打开资源压缩
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
            zipAlignEnabled true //是否使用zipalign工具压缩
        }
    }

    lintOptions {
        checkReleaseBuilds false
        abortOnError false
    }

    android.applicationVariants.all { variant ->
        variant.outputs.each { output ->
            def outputFile = output.outputFile
            if (outputFile != null && outputFile.name.endsWith('.apk')) {
                def fileName = "Town" + "_V${defaultConfig.versionName}_${releaseTime()}.apk"
                output.outputFile = new File(outputFile.parent, fileName)
            }
        }
    }
}
dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    testCompile 'junit:junit:4.12'
    //注解框架
    compile 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
    //网络请求库   OKhttp 3.8.1
    compile 'com.squareup.okhttp3:okhttp:3.8.1'
    //Gson 2.8.0
    compile 'com.google.code.gson:gson:2.8.0'
}
7.混淆

上面说的微信混淆工具只是对资源的混淆 , 混淆一是为了提高安全性 , 尽可能不易被人查看源代码 , 二是缩减体积 .下面附上 我自用的混淆代码;


 #-------------------------------------------定制化区域----------------------------------------------
#---------------------------------1.实体类---------------------------------
#-keep class 你的实体类所在的包.** { *; }
-keep class  com.townwang.town.model.javabean.** { *; }
#-------------------------------------------------------------------------

#---------------------------------2.第三方包-------------------------------
#okhttp
-dontwarn okhttp3.**
-keep class okhttp3.**{*;}
#okio
-dontwarn okio.**
-keep class okio.**{*;}
#butterknife:8.5.1
-keep class butterknife.** { *; }
-dontwarn butterknife.internal.**
-keep class **$$ViewBinder { *; }

-keepclasseswithmembernames class * {
    @butterknife.* <fields>;
}

-keepclasseswithmembernames class * {
    @butterknife.* <methods>;
}

##--------------- Gson ----------
-keep public class com.google.gson.**
-keep public class com.google.gson.** {public private protected *;}
-keepattributes Signature
-keepattributes *Annotation*
-keep public class www.zhihusan.agentscan.Model.Bean.LoginResultBean { private *; }
##---------------End: proguard configuration for Gson ----------
#picasso
#-dontwarn com.squareup.okhttp.**
#GrrenDao3.2.2
#-keepclassmembers class * extends org.greenrobot.greendao.AbstractDao {
#public static java.lang.String TABLENAME;
#}
#-keep class **$Properties

# If you do not use SQLCipher:
#-dontwarn org.greenrobot.greendao.database.**
# If you do not use Rx:
#-dontwarn rx.**
# Loading
#-keep class com.wang.avi.** { *; }
# -keep class com.wang.avi.indicators.** { *; }

#-------------------------------------------------------------------------

#---------------------------------3.与js互相调用的类------------------------


#---------------------------------内部类------------------------
#-keepclasseswithmembers class 你的类所在的包.父类$子类 { <methods>; }
#-------------------------------------------------------------------------

#---------------------------------4.反射相关的类和方法-----------------------



#----------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------

#-------------------------------------------基本不用动区域--------------------------------------------
#---------------------------------基本指令区----------------------------------
-optimizationpasses 5
-dontskipnonpubliclibraryclassmembers
-printmapping proguardMapping.txt
-optimizations !code/simplification/cast,!field/*,!class/merging/*
-keepattributes *Annotation*,InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
#----------------------------------------------------------------------------

#---------------------------------默认保留区---------------------------------
-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 * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
-keep class android.support.** {*;}
-keep public class * extends android.support.v4.app.Fragment
-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);
}
-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private static final java.io.ObjectStreamField[] serialPersistentFields;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    java.lang.Object writeReplace();
    java.lang.Object readResolve();
}
-keep class **.R$* {
 *;
}
-keepclassmembers class * {
    void *(**On*Event);
}
#----------------------------------------------------------------------------

#---------------------------------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);
}
#----------------------------------------------------------------------------
#---------------------------------------------------------------------------------------------------

8.优化字节码

ReDex是Facebook开源一个减小安卓app大小以提高性能的工具,内嵌以及清除僵尸代码这样的优化来减小字节码,主要是对Dex进行了优化,能让APK 运行更快,不过需要多测试是否会崩溃。

开源地址 : 适用于Android应用的字节码优化器

中文说明 (英文好的直接去看开源说明): 适用于Android应用的字节码优化器

这就是我目前所知道的优化了.也是我做一个APP所用到的.做个记录.有什么不对或者需要补充请留言.