eric lafortune - fighting application size with proguard and beyond

38
Fighting application size with ProGuard and beyond Eric Lafortune Developer of ProGuard and DexGuard Technical director at Saikoa www.saikoa.com

Upload: guardsquare

Post on 20-Jan-2017

382 views

Category:

Software


0 download

TRANSCRIPT

Page 1: Eric Lafortune - Fighting application size with ProGuard and beyond

Fighting application size

with ProGuard and beyond

Eric LafortuneDeveloper of ProGuard and DexGuard

Technical director at Saikoawww.saikoa.com

Page 2: Eric Lafortune - Fighting application size with ProGuard and beyond

Applications

Dalvikbytecode

Resources Assets

Nativecode

Renderscriptcode

App

Page 3: Eric Lafortune - Fighting application size with ProGuard and beyond

Dalvikbytecode

Resources Assets

Nativecode

Renderscriptcode

Technical constraints

AppGoogle Play Store: max 50 MB

Froyo/Gingerbread: 5 MB LinearAlloc buffer

Dex format: max 65536 method IDs

Page 4: Eric Lafortune - Fighting application size with ProGuard and beyond

Android build process

Page 5: Eric Lafortune - Fighting application size with ProGuard and beyond

Android build process

ApplicationJava bytecode

LibrariesJava bytecode

Dex Dalvik bytecode

JavacApplicationJava source

LibrariesJava bytecode

XML resources

Assets Assets

CompiledXML resourcesAapt

Page 6: Eric Lafortune - Fighting application size with ProGuard and beyond

Compress media

● Images: JPEG (lossy)

● Images: PNG

● Audio: AAC

● Video: H264 AVC

aapt crunch -S input_dir -C output_diraapt crunch -S input_dir -C output_dir

Page 7: Eric Lafortune - Fighting application size with ProGuard and beyond

Remove unused resources

● Lint (Android SDK)

● android-resource-remover (Philipp Berner)

● android-unused-resources (S. Kennedy)

● Gradle (Android SDK)

gradle lintgradle lint

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

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

Page 8: Eric Lafortune - Fighting application size with ProGuard and beyond

Splitting apk files

Page 9: Eric Lafortune - Fighting application size with ProGuard and beyond

Splitting apk files

ApplicationJava bytecode

LibrariesJava bytecode

Dex Dalvik bytecode

JavacApplicationJava source

LibrariesJava bytecode

XML resources

AssetsAssets

CompiledXML resourcesAapt

Dalvik bytecode

Assets

CompiledXML resources

Page 10: Eric Lafortune - Fighting application size with ProGuard and beyond

Splitting apk files

● Different variants in Gradle:android { ... productFlavors {

flavor1 { ignoreAssetsPattern '!*_large.png' assets.srcDirs = ['assets1'] res.srcDirs = ['res1'] }

flavor2 { ignoreAssetsPattern '!*_small.png' assets.srcDirs = ['assets2'] res.srcDirs = ['res2'] } }}

android { ... productFlavors {

flavor1 { ignoreAssetsPattern '!*_large.png' assets.srcDirs = ['assets1'] res.srcDirs = ['res1'] }

flavor2 { ignoreAssetsPattern '!*_small.png' assets.srcDirs = ['assets2'] res.srcDirs = ['res2'] } }}

Page 11: Eric Lafortune - Fighting application size with ProGuard and beyond

Splitting apk files

● Splits in Gradle:android { ... splits {

density { enable true reset() include 'mdpi', 'hdpi', 'xhdpi' }

abi { enable true reset() include 'armeabi-v7a', 'x86', 'mips' } }}

android { ... splits {

density { enable true reset() include 'mdpi', 'hdpi', 'xhdpi' }

abi { enable true reset() include 'armeabi-v7a', 'x86', 'mips' } }}

Page 12: Eric Lafortune - Fighting application size with ProGuard and beyond

Code size

Page 13: Eric Lafortune - Fighting application size with ProGuard and beyond

Code size

ApplicationJava bytecode

LibrariesJava bytecode

Dex Dalvik bytecode

JavacApplicationJava source

LibrariesJava bytecode

XML resources

Assets Assets

CompiledXML resourcesAapt

java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536java.lang.IllegalArgumentException: method ID not in [0, 0xffff]: 65536

Page 14: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex file

...

Strings

Types

Prototypes

Field IDs

Method IDs

Class definitions

Method IDs#0: Accounts . <init>(.....)#1: AccountManager . get(......)#2: AccountManager . …..#3: …..#4: …............................................#65535: …..

Page 15: Eric Lafortune - Fighting application size with ProGuard and beyond

ProGuard

Shrink

Androidruntime

Optimize Obfuscate

Androidruntime

Libraries

Applicationcode

Processedcode

Page 16: Eric Lafortune - Fighting application size with ProGuard and beyond

ProGuard

ApplicationJava bytecode

ProGuard

LibrariesJava bytecode

ProcessedJava bytecode Dex

JavacApplicationJava source

LibrariesJava bytecode

XML resources

Assets Assets

CompiledXML resourcesAapt

Dalvik bytecode

Page 17: Eric Lafortune - Fighting application size with ProGuard and beyond

-keepclassmembers,allowobfuscation class * { @dagger.** <fields>; @dagger.** <methods>;}

-keep class **$$ModuleAdapter-keep class **$$InjectAdapter-keep class **$$StaticInjection

-keep class com.example.DemoModule-keep class com.example.AndroidModule-keep class com.example.HomeActivity

-keepnames class dagger.Lazy

-keepclassmembers,allowobfuscation class * { @dagger.** <fields>; @dagger.** <methods>;}

-keep class **$$ModuleAdapter-keep class **$$InjectAdapter-keep class **$$StaticInjection

-keep class com.example.DemoModule-keep class com.example.AndroidModule-keep class com.example.HomeActivity

-keepnames class dagger.Lazy

Example configuration: Dagger

Generated classes

Corresponding base classes

Page 18: Eric Lafortune - Fighting application size with ProGuard and beyond

Libraries

Library Method IDs

Google Play Services 6.0 23,607

Guava 15.0 14,495

Yahoo Flurry Ads 4.1.0 8,511

ActionBarSherlock 4.4.0 4,300

RoboGuice 2.0 3,787

Facebook 3.5.2 3,166

Yahoo Flurry Analytics 4.1.0 1,148

Scala Core 2.11.2 50,786

Page 19: Eric Lafortune - Fighting application size with ProGuard and beyond

ProGuard on libraries

ProGuard

LibraryJava bytecode

Processed libraryJava bytecode

Page 20: Eric Lafortune - Fighting application size with ProGuard and beyond

Shrink Ads library-injars google-play-services.jar-outjars google-play-services-ads.jar

-libraryjars android-sdk/extras/android/support/v4/android-support-v4.jar-libraryjars android-sdk/platforms/android-20/android.jar

-dontoptimize-dontobfuscate-dontwarn com.google.**.R-dontwarn com.google.**.R$*-dontnote

-keep public class com.google.android.gms.ads.** { public protected *;}

-keep class com.google.android.gms.common.internal.safeparcel.SafeParcelable { java.lang.String NULL;}

-injars google-play-services.jar-outjars google-play-services-ads.jar

-libraryjars android-sdk/extras/android/support/v4/android-support-v4.jar-libraryjars android-sdk/platforms/android-20/android.jar

-dontoptimize-dontobfuscate-dontwarn com.google.**.R-dontwarn com.google.**.R$*-dontnote

-keep public class com.google.android.gms.ads.** { public protected *;}

-keep class com.google.android.gms.common.internal.safeparcel.SafeParcelable { java.lang.String NULL;}

proguard @ configuration.txtproguard @ configuration.txt

Command line:

configuration.txt

Page 21: Eric Lafortune - Fighting application size with ProGuard and beyond

Shrink Ads library

Before After Reduction

Size 3.3 M 578 K 83 %

Classes 3294 497 85 %

Method IDs 23607 3751 84 %

ProGuard

Google PlayServiceslibrary

Ads library

Page 22: Eric Lafortune - Fighting application size with ProGuard and beyond

Shrink Maps library-injars google-play-services.jar-outjars google-play-services-maps.jar

-libraryjars android-sdk/extras/android/support/v4/android-support-v4.jar-libraryjars android-sdk/platforms/android-20/android.jar

-dontoptimize-dontobfuscate-dontwarn com.google.**.R-dontwarn com.google.**.R$*-dontnote

-keep public class com.google.android.gms.maps.**, com.google.android.gms.common.**, com.google.android.gms.location.** { public protected *;}

-keep class com.google.android.gms.common.internal.safeparcel.SafeParcelable { java.lang.String NULL;}

-injars google-play-services.jar-outjars google-play-services-maps.jar

-libraryjars android-sdk/extras/android/support/v4/android-support-v4.jar-libraryjars android-sdk/platforms/android-20/android.jar

-dontoptimize-dontobfuscate-dontwarn com.google.**.R-dontwarn com.google.**.R$*-dontnote

-keep public class com.google.android.gms.maps.**, com.google.android.gms.common.**, com.google.android.gms.location.** { public protected *;}

-keep class com.google.android.gms.common.internal.safeparcel.SafeParcelable { java.lang.String NULL;}

proguard @ configuration.txtproguard @ configuration.txt

Page 23: Eric Lafortune - Fighting application size with ProGuard and beyond

Shrink Maps library

Before After Reduction

Size 3.3 M 583 K 82 %

Classes 3294 546 83 %

Method IDs 23607 4891 79 %

ProGuard

Google PlayServiceslibrary

Maps library

Page 24: Eric Lafortune - Fighting application size with ProGuard and beyond

Splitting dex files

Main dex file

Secondarydex file

Build-time Run-time

Page 25: Eric Lafortune - Fighting application size with ProGuard and beyond

Splitting dex files

ApplicationJava bytecode

LibrariesJava bytecode

DexMain

Dalvik bytecodeJavacApplicationJava source

LibrariesJava bytecode

XML resources

Assets Assets

CompiledXML resourcesAapt

SecondaryDalvik bytecode

Dex

Page 26: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: tools

● secondary-dex-gradle (Mohit Kanwal)

● Dex 65536 (Mmin18)

● DexGuard (Saikoa)

● multiDex (Android Gradle plugin 0.14)

Page 27: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: secondary-dex-gradle

● ApplicationProjectLibraryProject

● settings.gradle

● AndroidManifest.xml

● AppProject/src/com/example/App.javaAppProject/src/com/example/SecondaryDex.javaAppProject/src/com/example/FrameworkHack.java

include ':ApplicationProject', ':LibraryProject'include ':ApplicationProject', ':LibraryProject'

<application android:name="com.example.App" ... >

<application android:name="com.example.App" ... >

Page 28: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: secondary-dex-gradle

Advantages:

● It works!

Disadvantages:

● Split project

● No references library application→

● Dex classpath hack

Page 29: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: Dex 65536

● libs/google-play-services.jar (for example)

● custom_rules.xml

● AndroidManifest.xml

● src/com/example/App.java

... <pathtool libs="libs/google-play-services.jar" ... /> ...

... <pathtool libs="libs/google-play-services.jar" ... /> ...

<application android:name="com.example.App" ... >

<application android:name="com.example.App" ... >

new DexClassLoader(dexFile.getAbsolutePath(), dexOpt.getAbsolutePath(), nativeLibraryDir, classLoader.getParent());

new DexClassLoader(dexFile.getAbsolutePath(), dexOpt.getAbsolutePath(), nativeLibraryDir, classLoader.getParent());

Page 30: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: Dex 65536

Advantages:

● It works!

Disadvantages:

● Only for library jars

● Dex classloader hack

Page 31: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: DexGuard

● Gradle, Ant, Eclipse, Maven, Android Studio,...

● dexguard-project.txt

-splitdexfile com.google.**

-splitdexfile !com.facebook.samples.**, com.facebook.**

-splitdexfile com.google.**

-splitdexfile !com.facebook.samples.**, com.facebook.**

Page 32: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: DexGuard

Advantages:

● Transparent

● Flexible

● Lazy loading

Disadvantages:

● Reflection

Page 33: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: multiDex

Android 5.0 loads classes2.dex, classes3.dex, etc.

● build.gradle

● AndroidManifest.xml (for legacy platforms)

dependencies { compile 'com.android.support:multidex:1.0.0'}

android { ... defaultConfig { multiDexEnabled true }}

dependencies { compile 'com.android.support:multidex:1.0.0'}

android { ... defaultConfig { multiDexEnabled true }}

<application android:name="android.support.multidex.MultiDexApplication" ... >

<application android:name="android.support.multidex.MultiDexApplication" ... >

Page 34: Eric Lafortune - Fighting application size with ProGuard and beyond

Dex splitting: Android 5.0

Advantages:

● Latest standard

Disadvantages:

● Eager loading

Page 35: Eric Lafortune - Fighting application size with ProGuard and beyond

Summary

Resources and assets:

– Compress

– Trim

– Split apk

Bytecode:

– Shrink libraries

– Shrink application

– Split dex file

Page 36: Eric Lafortune - Fighting application size with ProGuard and beyond

Further reading

● “Custom Class Loading in Dalvik”, Fred Chung, Googlehttp://android-developers.blogspot.com/2011/07/custom-class-loading-in-dalvik.html

● “Play Services 5.0 Is A Monolith Abomination”, Jake Whartonhttp://jakewharton.com/play-services-is-a-monolith/

● “DEX Sky’s the limit? No, 65K methods is”, Sebastiano Gottardohttps://medium.com/@rotxed/dex-skys-the-limit-no-65k-methods-is-28e6cb40cf71

● “Multi-dex support”, Xavier Ducrohethttps://plus.google.com/+XavierDucrohet/posts/1FnzwdcBnyC

● “Under the Hood: Dalvik patch for Facebook for Android”, David Reisshttps://www.facebook.com/notes/facebook-engineering/under-the-hood-dalvik-patch-for-facebook-for-android/10151345597798920

● “Remove unused resources”, Tor Norbyehttps://plus.google.com/+TorNorbye/posts/eHsStybrrBf

● “Tips for reducing APK file size”, David Karlsson, Sonyhttp://developer.sonymobile.com/2012/01/31/tips-for-reducing-apk-file-size/

Page 37: Eric Lafortune - Fighting application size with ProGuard and beyond

Software tools

● android-resource-remover, Philipp Berner, KeepSafehttps://github.com/KeepSafe/android-resource-remover

● android-unused-resources, S. Kennedyhttps://code.google.com/p/android-unused-resources/

● secondary-dex-gradle, Mohit Kanwalhttps://github.com/creativepsyco/secondary-dex-gradle

● Dex 65536, Mmin18https://github.com/mmin18/Dex65536

● ProGuardhttp://proguard.sourceforge.net/

● DexGuardhttp://www.saikoa.com/dexguard

Page 38: Eric Lafortune - Fighting application size with ProGuard and beyond

Questions?

Open source

Java bytecode

ProGuard

Saikoa

DexGuard

Dalvik bytecode

Shrinking

Optimization