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 t
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.
Thanks to Petr Nalevka's insightful answer I have come up with the following solution (a.k.a. ugly hack).
This code removes all PNG drawables under hdpi, xhdpi, xxhdpi, and so on, keeping only the drawables under mdpi (the ones with the smallest file size). Drawables are only removed if they have a sibling under mdpi, that is if they can be safely removed. Google Play Services should remain fully functional.
A task gets registered for each application variant, so this code works with multiple product flavors.
applicationVariants.all { variant ->
def variantCamelName = variant.name.substring(0, 1).toUpperCase() + variant.name.substring(1)
def stripResourcesTask = task("strip${variantCamelName}Resources") {
doFirst {
def resDir = "$buildDir/intermediates/res/merged/$variant.dirName"
def gpsDir = "$buildDir/intermediates/exploded-aar/com.google.android.gms"
fileTree(dir: "$gpsDir", include: "**/drawable-*/*.png").each { resFile ->
if (!resFile.parentFile.name.endsWith("-mdpi")) {
def resName = resFile.name
def resDirName = resFile.parentFile.name
def mdpiFile = "$resFile.parentFile.parentFile/drawable-mdpi/$resName"
if (file(mdpiFile).file) {
def files = fileTree(dir: "$resDir", include: "**/${resDirName}/${resName}")
// files.each { f -> println "Deleting $f..." }
delete files
}
}
}
}
}
tasks["merge${variantCamelName}Resources"].finalizedBy stripResourcesTask
}
Tested with Android Studio 1.3.0, Gradle plugin version 1.3.0, build tools version 22.0.1 and GPS version 7.5.0.
I'm a bit of a Groovy noob, so this is probably not the most elegant solution.
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.