Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filtering resources from the Play Services monolith to make your APK smaller

A lot has been written about the monolithic nature of the Google Play Services and why it should be split into more libraries. For now the workaround to keep your APK small is to use proguard to strip out unused references. This works pretty well for classes.dex, but not for the included resources.

I get around 1 MB of additional unused resources and with a bundled Android Wear app this overhead doubles. So my APK is 2 MB larger than needed.

I'm wondering whether there is some straightforward way in Gradle to exclude some resources coming from the dependencies AARs from the resulting APK.

It seems that the AAPT options in the Gradle Android plugin only allow filtering assets.

I was thinking about hooking in some custom aapt script which would call remove for a list of resources using aapt before signing the APK for release.

Does someone else have a simpler solution?

like image 636
Petr Nalevka Avatar asked Jul 12 '14 21:07

Petr Nalevka


2 Answers

After some research I found the following sub-optimal solution. I had to list all the not needed resources manually (luckily patterns are applicable) and make sure also to delete all files where they are referenced. Below is an example which makes my Wear app APK from 1.5 MB into 300kb and the APK is working normally without issues.

I had to create my own task stripResources and hook it between standard Android plug-in tasks: mergeReleaseResources and processReleaseResources.

task stripResources << {
    println "Custom resource stripping in: $buildDir"
    delete fileTree(dir: "$buildDir", include: "**/layout/confirmation_activity_layout.xml")
    delete fileTree(dir: "$buildDir", include: "**/layout/watch_card_content.xml")
    delete fileTree(dir: "$buildDir", include: "**/common_signin*.png")
    delete fileTree(dir: "$buildDir", include: "**/drawable/common_signin*.xml")
    delete fileTree(dir: "$buildDir", include: "**/generic_confirmation*.png")
    delete fileTree(dir: "$buildDir", include: "**/drawable/confirmation_*.xml")
    delete fileTree(dir: "$buildDir", include: "**/drawable/card_background.xml")
    delete fileTree(dir: "$buildDir", include: "**/card_frame*.png")
    delete fileTree(dir: "$buildDir", include: "**/go_to*.png")
    delete fileTree(dir: "$buildDir", include: "**/drawable/go_to_*.xml")
    delete fileTree(dir: "$buildDir", include: "**/ic_plusone*.png")
    delete fileTree(dir: "$buildDir", include: "**/powered_by_google*.png")
    // if you only have English you can teh following to filter out some GPS texts wich also take few hundreds of kb
    // delete fileTree(dir: "$buildDir", include: "**/values-*/values.xml")
}

    tasks.whenTaskAdded { task ->
        if (task.name == 'processReleaseManifest') {
            task.dependsOn stripResources
        }
    }

You can do a similar task for regular Android APK.

like image 182
Petr Nalevka Avatar answered Oct 02 '22 05:10

Petr Nalevka


As of version 0.14 of the Android Gradle plugin, this can be accomplished automatically as mentioned in this post:

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
        }
    }
}

shrinkResources is a flag that tells the compiler to skip any resources that aren't referenced. minifyEnabled is the new name for runProguard, which must be enabled for shrinkResources to work.

like image 41
Jarett Millard Avatar answered Oct 02 '22 06:10

Jarett Millard