Secure and smaller APK size using Proguard

Proguard is a tool which shrinks, optimizes, and obfuscates code. It detects and removes unused classes, fields, methods and attributes from APK. It optimizes bytecode and removes unused instructions. It renames classes, fields, and methods via meaningless names.


Why Proguard for Android?

Shrinking contribute towards a work around for 64k reference limit of Android Application.

Optimization analyzes and optimizes the bytecode of the methods.

The obfuscated code makes APK difficult to reverse engineer, which is especially valuable for app security.

The result of above all is smaller APK size and more difficult to reverse engineer.


Enable Proguard in Android Studio

To enable code shrinking with ProGuard, add minifyEnabled true to the appropriate build type in your build.gradle file.

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

To enable ProGuard, encounter unexpected crashes or situations so it is very important to enable this on final APK testing.

In the above code we enabled the minifyEnabled property in the gradle file. Other than that proguardFiles property defines the proguard rules:

1.     The getDefaultProguardFile() refers default file “proguard-android.txt” which gets from the Android SDK tools/proguard/ folder. You can also use “proguard-android-optimize.txt” file for more code shrinking located on the same folder.

2.     The “proguard-rules.pro” file is where you can add custom ProGuard rules. It is located at the root of the project module.


Proguard Outputs

With each build, ProGuard outputs the below files (located under <module>/build/outputs/ folder):

dump.txt - Describes the internal structure of all the class files in the APK.

mapping.txt -Provides mapping between the original and obfuscated class, 
method, and field names.

seeds.txt - Lists the classes and members that were not obfuscated.

usage.txt - Lists the code that was removed from the APK.

Here seeds.txt and mapping.txt are very important files. With the help of above files you can analyze you application package and debug any mapping issue.


Custom Rules

In general you do not want native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files to be a part of proguard process.  You can create your own rules in the proguard-rules.pro file to achieve this.

-keep option specify force ProGuard to keep certain code.

For example:
-keep public class MyClass

Alternatively, you can add the @Keep annotation to the code you want to keep. This annotation is available only when using the Annotations Support Library. 

In general you have to keep all fundamental classes that may be referenced by the AndroidManifest.xml file of the application. If your manifest file contains other classes and methods, you may have to specify those as well.

-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose

# Optimization is turned off by default. Dex does not like code run
# through the ProGuard optimize and preverify steps (and performs some
# of these optimizations on its own).
-dontoptimize
-dontpreverify
# Note that if you want to enable optimization, you cannot just
# include optimization flags in your own project configuration file;
# instead you will need to point to the
# "proguard-android-optimize.txt" file instead of this one from your
# project.properties file.

-keepattributes *Annotation*
-keep public class com.google.vending.licensing.ILicensingService
-keep public class com.android.vending.licensing.ILicensingService

# For native methods, see http://proguard.sourceforge.net/manual/examples.html#native
-keepclasseswithmembernames class * {
    native <methods>;
}

# keep setters in Views so that animations can still work.
# see http://proguard.sourceforge.net/manual/examples.html#beans
-keepclassmembers public class * extends android.view.View {
   void set*(***);
   *** get*();
}

# We want to keep methods in Activity that could be used in the XML attribute onClick
-keepclassmembers class * extends android.app.Activity {
   public void *(android.view.View);
}

# For enumeration classes, see http://proguard.sourceforge.net/manual/examples.html#enumerations
-keepclassmembers enum * {
    public static **[] values();
    public static ** valueOf(java.lang.String);
}

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

-keepclassmembers class **.R$* {
    public static <fields>;
}

# The support library contains references to newer platform versions.
# Don't warn about those in case this app is linking against an older
# platform version.  We know about them, and they are safe.
-dontwarn android.support.**

# Understand the @Keep support annotation.
-keep class android.support.annotation.Keep

-keep @android.support.annotation.Keep class * {*;}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <methods>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <fields>;
}

-keepclasseswithmembers class * {
    @android.support.annotation.Keep <init>(...);



Below are the example rules to keep all public, protected classes, class members, native method names and serialization code:

// Keep only TestInterface
-keep public interface com.androidjavapoint.TestInterface

// Keep all public and protected class
-keep public class * { 
      public protected *; 

// Keep class members
-keepclassmembernames class * { 
    java.lang.Class class$(java.lang.String); 
    java.lang.Class class$(java.lang.String, boolean); 

// Keep class members and native method
-keepclasseswithmembernames,includedescriptorclasses class * { 
    native <methods>; 

// Keep ENUM
-keepclassmembers,allowoptimization enum * { 
    public static **[] values(); public static ** valueOf(java.lang.String); 

// Keep Serializable classes and members
-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(); 


Drawbacks

Below are some drawbacks to use Proguard-
Not properly configured rules can cause the app crash.
Stack traces are difficult to understand because of obfuscation.


Conclusion

Hope this guide helps you to understand Proguard. For more aggressive optimization with fewer exclusionary rules please refer official documentation for more information.


 To find more interesting topics on Software development follow me at https://medium.com/@ankit.sinhal

You can also find my Android Applications on play store




Comments

Post a Comment

Popular posts from this blog

Android Performance: Avoid using ENUM on Android

Smart way to update RecyclerView using DiffUtil

Android O: Impact On Running Apps And Developer Viewpoint