17

I have installed Google Play Services and created a Hello World app to test that everything is working fine and I think the application size is too big: 4.98 MB. I'm using Android Studio and I've followed the instructions detailed in the android developers web.

This is my gradle file:

apply plugin: 'android'

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.1"

    defaultConfig {
        minSdkVersion 15
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.google.android.gms:play-services:4.3.23'
}

EDIT

This is my proguard file:

-keep class * extends java.util.ListResourceBundle {
    protected Object[][] getContents();
}

-keep public class com.google.android.gms.common.internal.safeparcel.SafeParcelable {
    public static final *** NULL;
}

-keepnames @com.google.android.gms.common.annotation.KeepName class *
-keepclassmembernames class * {
    @com.google.android.gms.common.annotation.KeepName *;
}

-keepnames class * implements android.os.Parcelable {
   public static final ** CREATOR;
}

** EDIT 2 **

I've installed Google Play Services using the last version of Intellij Idea, and now the apk is 3.52 MB. I don't know if this is an acceptable size.

Is this normal?

Marcos
  • 4,643
  • 7
  • 33
  • 60

2 Answers2

15

No, that's not normal - earlier builds of my GPSTest app that included Google Play Services for maps were only 808KB after being obfuscated using Proguard- current version (after adding another library) is around 1,497KB after obfuscation.

I would recommend exporting the APK from the command line using the following steps to avoid potential issues with Android Studio:

  1. Go to root of the project at the command line
  2. Run gradlew assembleRelease
  3. Find the signed and obfuscated APK in /app/build/apk folder

If you are exporting the APK via Android Studio, be aware that there is a known issue where Android Studio will export using the assembleDebug task instead of the assembleRelease task by default. As a result, any configurations in your build.gradle file for running Proguard that are specific to the release buildType won't be executed.

As a workaround for exporting via Android Studio, you can change the default Build Variant via the following steps:

  1. In Android Studio, open "View->Tool Windows->Build Variants"
  2. In the window that opens, change "Build Variant" from debug to release.

Now when you do "Build->Generate Signed APK...", Android Studio should run the release Build Variant, which should run Proguard if you have it configured correctly in build.gradle. You can change back to debug variant while debugging your app on a normal basis.

If you want to replicate my settings from GPSTest, here's the proguard.cfg:

-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.preference.Preference

-keepclasseswithmembers class * {
    native <methods>;
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet);
}

-keepclasseswithmembers class * {
    public <init>(android.content.Context, android.util.AttributeSet, int);
}

-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

-keep class * implements android.os.Parcelable {
  public static final android.os.Parcelable$Creator *;
}

-keep class * extends java.util.ListResourceBundle {
    protected Object[][] getContents();
}

-dontwarn **CompatHoneycomb
-dontwarn **CompatCreatorHoneycombMR2
-dontwarn **AccessibilityServiceInfoCompatJellyBeanMr2
-dontwarn android.support.v4.view.**
-dontwarn android.support.v4.media.**
-dontwarn com.actionbarsherlock.internal.**
-keep class android.support.v4.** { *; }
-keepattributes *Annotation*
-keep public class * extends android.view.View
-keep public class * extends android.view.ViewGroup
-keep public class * extends android.support.v4.app.Fragment

-keepclassmembers class * extends com.actionbarsherlock.ActionBarSherlock {
    <init>(android.app.Activity, int);
}

... and build.gradle:

apply plugin: 'android'

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.0"

    defaultConfig {
        minSdkVersion 8
        targetSdkVersion 19
    }

    if (project.hasProperty("secure.properties")
            && new File(project.property("secure.properties")).exists()) {

        Properties props = new Properties()
        props.load(new FileInputStream(file(project.property("secure.properties"))))

        signingConfigs {
            debug {
                storeFile file("gpstest.debug.keystore")
            }

            release {
                storeFile file(props['key.store'])
                keyAlias props['key.alias']
                storePassword "askmelater"
                keyPassword "askmelater"
            }
        }
    } else {
        signingConfigs {
            debug {
                storeFile file("gpstest.debug.keystore")
            }

            release {
                // Nothing here
            }
        }
    }

    buildTypes {
        release {
            runProguard true
            proguardFile 'proguard.cfg'
            signingConfig signingConfigs.release
        }
    }
}

task askForPasswords << {
    // Must create String because System.readPassword() returns char[]
    // (and assigning that below fails silently)
    def storePw = new String(System.console().readPassword("\nKeystore password: "))
    def keyPw = new String(System.console().readPassword("Key password: "))

    android.signingConfigs.release.storePassword = storePw
    android.signingConfigs.release.keyPassword = keyPw
}

tasks.whenTaskAdded { theTask ->
    if (theTask.name.equals("packageRelease")) {
        theTask.dependsOn "askForPasswords"
    }
}

dependencies {
    compile project(':ShowcaseViewLibrary')
    compile 'com.google.android.gms:play-services:3.2.65'
    compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'
    compile 'org.jraf:android-switch-backport:1.2'
    compile 'com.google.maps.android:android-maps-utils:0.2.1'
}

Entire GPSTest source code is available on Github if you want to use it as a sample.

EDIT

Another way to help shrink your APK when using features from Google Play Services v6.5 or higher is to include only the library for the feature in Google Play Services that you're actually using.

For example, if the only Google Play Services API you're using is the Maps API v2, instead of including the entire Google Play Services library in build.gradle:

compile 'com.google.android.gms:play-services:7.8.0'

...you can just include the Maps API v2 portion:

compile 'com.google.android.gms:play-services-maps:7.8.0'

See the Google Play Services - "Selectively compiling APIs into your executable" section for details on what APIs you can split out. Here's a list as of Sept. 2015:

  • Google+ com.google.android.gms:play-services-plus:7.8.0
  • Google Account Login com.google.android.gms:play-services-identity:7.8.0
  • Google Actions, Base Client Library com.google.android.gms:play-services-base:7.8.0
  • Google App Indexing com.google.android.gms:play-services-appindexing:7.8.0
  • Google App Invites com.google.android.gms:play-services-appinvite:7.8.0
  • Google Analytics com.google.android.gms:play-services-analytics:7.8.0
  • Google Cast com.google.android.gms:play-services-cast:7.8.0
  • Google Cloud Messaging com.google.android.gms:play-services-gcm:7.8.0
  • Google Drive com.google.android.gms:play-services-drive:7.8.0
  • Google Fit com.google.android.gms:play-services-fitness:7.8.0
  • Google Location, Activity Recognition, and Places com.google.android.gms:playservices-location:7.8.0
  • Google Maps com.google.android.gms:play-services-maps:7.8.0
  • Google Mobile Ads com.google.android.gms:play-services-ads:7.8.0
  • Mobile Vision com.google.android.gms:play-services-vision:7.8.0
  • Google Nearby com.google.android.gms:play-services-nearby:7.8.0
  • Google Panorama Viewer com.google.android.gms:play-services-panorama:7.8.0
  • Google Play Game services com.google.android.gms:play-services-games:7.8.0
  • SafetyNet com.google.android.gms:play-services-safetynet:7.8.0
  • Google Wallet com.google.android.gms:play-services-wallet:7.8.0
  • Android Wear com.google.android.gms:play-services-wearable:7.8.0
Sean Barbeau
  • 11,496
  • 8
  • 58
  • 111
  • Thanks. I see you have `proguard.cfg` and mine is `proguard-rules.txt`. Maybe they are different files? I don't have any other proguard file. – Marcos Mar 26 '14 at 22:46
  • I've created a `proguard.cfg` like yours (not exactly the same) and updated `build.gradle` but still getting the same apk size. Another question, Google Play Services require the Android Support Library? Because it is included in the External Libraries of my project. – Marcos Mar 26 '14 at 23:16
  • You are using `gradlew assembleRelease` to package APK? I don't believe you need to include support library just because you are using Google Play Services. – Sean Barbeau Mar 26 '14 at 23:46
  • How I can know if I'm using `gradlew assembleRelease`? I'm not using the terminal. – Marcos Mar 27 '14 at 00:00
  • Try running `gradlew assembleRelease` from the command line from the root of your project, and find the APK in the /profile/build/APK folder. See if its the same size - if not, it means its not a Gradle issue but the way you are exporting the APK in Studio. – Sean Barbeau Mar 27 '14 at 01:20
  • 1
    Ok, I've done it and my apk (in app/build/apk) is 725KB (is release-unaligned). If I install from Android Studio the debug-unaligned is about 1.3MB. – Marcos Mar 27 '14 at 01:36
  • Ok, good. So your project files are fine, Studio just isnt running Proguard when you export from the UI. I'm not in front of a computer right now but I'll take a look tomorrow (I export from command line). – Sean Barbeau Mar 27 '14 at 01:42
  • @enrmarc There is a known issue with exporting signed APKs from Android Studio (https://code.google.com/p/android/issues/detail?id=56532), where the release buildType isn't selected by default, and as a result Proguard wouldn't be executed. I've updated my answer with instructions for exporting from command line (recommended method), but also included workaround for exporting from Android Studio. Please try this and let me know if it works. – Sean Barbeau Mar 27 '14 at 15:23
  • 1
    Thanks @Sean. Now I've fixed it! But doing a different thing (your answer was very inspiring. I've posted the answer. – Marcos Mar 27 '14 at 16:02
2

Ok, finally I've encountered the problem (thanks to @Sean Barbeau). It was the build.gradlefile of my module. In the BuildTypessection I had releaseinstead of debug and I was deploying the debug version ... I've changed that and now my app is only 1.17 MB !

buildTypes {
    debug {
        runProguard true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
    }
}

EDIT

As @SeanBarbeau pointed, runProguardis set to true in debug mode. If I set falsethen my app is again big (5 MB of size).

Marcos
  • 4,643
  • 7
  • 33
  • 60
  • 1
    Well, you can do this, but you'll be running Proguard every time you launch the app (i.e., after every code change), since you've specified to use Proguard in the assembleDebug Build Type. I really don't think you want to do this (hence why Gradle supports separate debug and release Build Types) – Sean Barbeau Mar 27 '14 at 16:07
  • 1
    Oh, you are right. Now I have `runProguard false`and the app is 5 MB. Mierda. – Marcos Mar 27 '14 at 16:10
  • Yeah :) (I speak un pocito Espanol). I think the workaround in my answer is the "right" way to handle this until they make it default to use release build Type when exporting APK in Studio. You could flip "runProguard" from true to false when debugging using this solution, then to true when exporting, but this is messy and would interfere with version control. I think its best to change setting in Studio. – Sean Barbeau Mar 27 '14 at 16:14
  • Yes, thank you so much @SeanBarbeau. I think I will have to read some tutorial of gradle before start my little project :) – Marcos Mar 27 '14 at 16:15