Examples

tip The Ant/Gradle/Eclipse/Maven build processes of DexGuard automatically provide configurations that should work for basic projects. Notably, they already support the following libraries (through lib/dexguard-common.pro):

You can browse through this section if you are creating a build process from scratch, or if your project requires further customization.

Some typical useful configurations:

  1. A simple Android activity
  2. A complete Android application
  3. Scala applications with the Scala runtime
  4. A typical library
  5. Processing native methods
  6. Processing callback methods
  7. Processing enumeration classes
  8. Processing serializable classes
  9. Processing bean classes
  10. Processing annotations
  11. Processing database drivers
  12. Processing resource injection
  13. Processing resource files
  14. Processing manifest files
  15. Adding reflection for sensitive APIs
  16. Encrypting classes
  17. Encrypting strings
  18. Producing useful obfuscated stack traces
  19. Obfuscating package names
  20. Publishing on the Samsung app market
  21. Removing logging code
  22. Restructuring the output archives
  23. Filtering the input and the output
  24. Processing multiple applications at once
  25. Finding dead code
  26. Using annotations to configure DexGuard
You can find some sample configuration files in the examples directory of the DexGuard distribution.

A simple Android activity

tip The standard DexGuard build process already specifies this configuration for you.

These options shrink, optimize, obfuscate, and convert the single Android activity mypackage.MyActivity:

-injars      bin/classes
-injars      bin/resources.ap_
-injars      libs
-outjars     bin/application.apk
-libraryjars /usr/local/android-sdk/platforms/android-15/android.jar

-dalvik
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic

-keep public class mypackage.MyActivity

We're targeting the Android run-time and keeping the activity as an entry point.

We're targeting Android's Dalvik virtual machine, converting the processed class files straight to the Dex format, with the -dalvik option. This way, we don't need the separate dx compiler anymore.

The -optimizations option disables some arithmetic simplifications that Dalvik 1.0 and 1.5 can't handle. Note that the Dalvik VM also can't handle aggressive overloading (of static fields).

If applicable, you should add options for processing native methods, callback methods, enumerations, annotations, and resource files.

A complete Android application

tip The standard DexGuard build process already specifies this configuration for you.

If you're constructing a build process from scratch: these options shrink, optimize, and obfuscate all public activities, services, broadcast receivers, and content providers from the compiled classes and external libraries:

-injars      bin/classes
-injars      bin/resources.ap_
-injars      libs
-outjars     bin/application.apk
-libraryjars /usr/local/android-sdk/platforms/android-15/android.jar

-dalvik
-repackageclasses ''
-allowaccessmodification
-optimizations !code/simplification/arithmetic
-keepattributes *Annotation*
-keepresourcexmlattributenames
    manifest/installLocation,
    manifest/versionCode,
    manifest/application/*/intent-filter/*/name

-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider

-keep public class * extends android.view.View {
    public <init>(android.content.Context);
    public <init>(android.content.Context, android.util.AttributeSet);
    public <init>(android.content.Context, android.util.AttributeSet, int);
    public void set*(...);
}

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

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

-keepclassmembers class * extends android.content.Context {
   public void *(android.view.View);
   public void *(android.view.MenuItem);
}

-keepclassmembers class * implements android.os.Parcelable {
    static ** CREATOR;
}

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

-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}

Most importantly, we're keeping 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.

We're keeping annotations, since they might be used by custom RemoteViews.

We're keeping some attribute names in the binary Android manifest file. The Android runtime somewhat arbitrarily requires them to launch the application. Names of other attributes that have numeric identifiers can be discarded.

We're keeping any custom View extensions and other classes with typical constructors, since they might be referenced from XML layout files.

We're also keeping possible onClick handlers in custom Context extensions, since they might be referenced from XML layout files.

We're also keeping the required static fields in Parcelable implementations, since they are accessed by introspection.

We're keeping the static fields of referenced inner classes of auto-generated R classes, just in case your code is accessing those fields by introspection. Note that the compiler already inlines primitive fields, so DexGuard can generally remove all these classes entirely anyway (because the classes are not referenced and therefore not required).

Finally, we're keeping annotated Javascript interface methods, so they can be exported and accessed by their original names. Javascript interface methods that are not annotated (in code targeted at Android versions older than 4.2) still need to be preserved manually.

If you're using additional Google APIs, you'll have to specify those as well, for instance:

-libraryjars /usr/local/android-sdk/add-ons/google_apis-7_r01/libs/maps.jar

If you're using Google's optional License Verification Library, you can obfuscate its code along with your own code. You do have to preserve its ILicensingService interface for the library to work:

-keep public interface com.android.vending.licensing.ILicensingService

If you're using the Android Compatibility library, you should add the following line, to let DexGuard know it's ok that the library references some classes that are not available in all versions of the API:

-dontwarn android.support.**

If applicable, you should add options for processing native methods, callback methods, enumerations, and resource files. You may also want to add options for producing useful stack traces and to remove logging. You can find a complete sample configuration in examples/android.pro in the DexGuard distribution.

Applications with the Scala runtime

These additional options shrink, optimize, and obfuscate Scala applications in bin/classes:
-injars      bin/classes
-injars      bin/resources.ap_
-injars      libs
-injars      /usr/local/scala-2.9.1/lib/scala-library.jar
-outjars     bin/application.apk
-libraryjars /usr/local/android-sdk/platforms/android-15/android.jar

// ... Add the standard settings for Android applications ...

-dontwarn scala.**

-keep class * implements org.xml.sax.EntityResolver

-keepclassmembers class * {
    ** MODULE$;
}

-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinPool {
    long eventCount;
    int  workerCounts;
    int  runControl;
    scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode syncStack;
    scala.concurrent.forkjoin.ForkJoinPool$WaitQueueNode spareStack;
}

-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinWorkerThread {
    int base;
    int sp;
    int runState;
}

-keepclassmembernames class scala.concurrent.forkjoin.ForkJoinTask {
    int status;
}

-keepclassmembernames class scala.concurrent.forkjoin.LinkedTransferQueue {
    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference head;
    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference tail;
    scala.concurrent.forkjoin.LinkedTransferQueue$PaddedAtomicReference cleanMe;
}

The configuration is an extension of the configuration for processing activities, because Scala is compiled to ordinary Java bytecode. However, the example processes the Scala runtime library as well. The processed jar can be an order of magnitude smaller and a few times faster than the original code (for the Scala code examples, for instance).

The -dontwarn option tells DexGuard not to complain about some artefacts in the Scala runtime, the way it is compiled by the scalac compiler (at least in Scala 2.9.1 and older). Note that this option should always be used with care.

The additional -keep options make sure that some classes and some fields that are accessed by means of introspection are not removed or renamed.

If applicable, you should add options for processing native methods, callback methods, enumerations, serializable classes, bean classes, annotations, and resource files.

A typical library

tip The standard DexGuard build process already specifies this configuration for you.

If you're constructing a build process from scratch: these options shrink, optimize, and obfuscate an entire library, keeping all public and protected classes and class members, native method names, and serialization code. The processed version of the library can then still be used as such, for developing code based on its public API.

-injars       in.jar
-outjars      out.jar
-libraryjars  /usr/local/android-sdk/platforms/android-15/android.jar
-printmapping out.map

-keepparameternames
-renamesourcefileattribute SourceFile
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

-keep public class * {
    public protected *;
}

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

-keepclasseswithmembernames,includedescriptorclasses class * {
    native <methods>;
}

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

-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();
}

This configuration should preserve everything we'll ever want to access in the library. Only if there are any other non-public classes or methods that are invoked dynamically, they should be specified using additional -keep options.

The -keepclassmembernames option for the class$ methods is not strictly necessary. These methods are inserted by the javac compiler and the jikes compiler respectively, in JDK 1.2 and older, to implement the .class construct. DexGuard will automatically detect them and deal with them, even when their names have been obfuscated. However, other obfuscators may rely on the original method names. It may therefore be helpful to preserve them, in case these other obfuscators are ever used for further obfuscation of the library.

The "Exceptions" attribute has to be preserved, so the compiler knows which exceptions methods may throw.

The "InnerClasses" attribute (or more precisely, its source name part) has to be preserved too, for any inner classes that can be referenced from outside the library. The javac compiler would be unable to find the inner classes otherwise.

The "Signature" attribute is required to be able to access generic types when compiling in JDK 5.0 and higher.

The -keepparameternames option keeps the parameter names in the "LocalVariableTable" and "LocalVariableTypeTable" attributes of public library methods. Some IDEs can present these names to the developers who use the library.

Finally, we're keeping the "Deprecated" attribute and the attributes for producing useful stack traces.

We've also added some options for for processing native methods, enumerations, serializable classes, and annotations, which are all discussed in their respective examples.

Processing native methods

tip The standard DexGuard build process already specifies this configuration for you.

If your application contains native methods, you'll want to preserve their names and their classes' names, so they can still be linked to the native library. The following additional option will ensure that:

-keepclasseswithmembernames,includedescriptorclasses class * {
    native <methods>;
}

Note the use of -keepclasseswithmembernames. We don't want to preserve all classes or all native methods; we just want to keep the relevant names from being obfuscated. The modifier includedescriptorclasses additionally makes sure that the return types and parameter types aren't renamed either, so the entire signatures remain compatible with the native libraries.

DexGuard doesn't look at your native code, so it won't automatically preserve the classes or class members that are invoked by the native code. These are entry points, which you'll have to specify explicitly. Callback methods are discussed below as a typical example.

Processing callback methods

If your application contains callback methods, which are called from external code (native code, scripts,...), you'll want to preserve them, and probably their classes too. They are just entry points to your code, much like, say, the main method of an application. If they aren't preserved by other -keep options, something like the following option will keep the callback class and method:
-keep class mypackage.MyCallbackClass {
    void myCallbackMethod(java.lang.String);
}

This will preserve the given class and method from being removed or renamed.

Processing enumeration classes

tip The standard DexGuard build process already specifies this configuration for you.

If your application contains enumeration classes, you'll have to preserve some special methods. Enumerations were introduced in Java 5. The java compiler translates enumerations into classes with a special structure. Notably, the classes contain implementations of some static methods that the run-time environment accesses by introspection (Isn't that just grand? Introspection is the self-modifying code of a new generation). You have to specify these explicitly, to make sure they aren't removed or obfuscated:

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

Processing serializable classes

More complex applications may contain classes that are serialized. Depending on the way in which they are used, they may require special attention:

Note that the above options may preserve more classes and class members than strictly necessary. For instance, a large number of classes may implement the Serialization interface, yet only a small number may actually ever be serialized. Knowing your application and tuning the configuration often produces more compact results.

Processing bean classes

If your application makes extensive use of introspection on bean classes to find bean editor classes, or getter and setter methods, then configuration may become painful. There's not much else you can do than making sure the bean class names, or the getter and setter names don't change. For instance:
-keep public class mypackage.MyBean {
    public void setMyProperty(int);
    public int getMyProperty();
}

-keep public class mypackage.MyBeanEditor

If there are too many elements to list explicitly, wildcards in class names and method signatures might be helpful. This example preserves all possible setters and getters in classes in the package mybeans:

-keep class mybeans.** {
    void set*(***);
    void set*(int, ***);

    boolean is*(); 
    boolean is*(int);

    *** get*();
    *** get*(int);
}

The '***' wildcard matches any type (primitive or non-primitive, array or non-array). The methods with the 'int' arguments matches properties that are lists.

Processing annotations

tip The standard DexGuard build process already specifies this configuration for you.

If your application uses annotations, you may want to preserve them in the processed output. Annotations are represented by attributes that have no direct effect on the execution of the code. However, their values can be retrieved through introspection, allowing developers to adapt the execution behavior accordingly. By default, DexGuard treats annotation attributes as optional, and removes them in the obfuscation step. If they are required, you'll have to specify this explicitly:

-keepattributes *Annotation*

For brevity, we're specifying a wildcarded attribute name, which will match RuntimeVisibleAnnotations, RuntimeInvisibleAnnotations, RuntimeVisibleParameterAnnotations, RuntimeInvisibleParameterAnnotations, and AnnotationDefault. Depending on the purpose of the processed code, you could refine this selection, for instance not keeping the run-time invisible annotations (which are only used at compile-time).

Some code may make further use of introspection to figure out the enclosing methods of anonymous inner classes. In that case, the corresponding attribute has to be preserved as well:

-keepattributes EnclosingMethod

Processing database drivers

Database drivers are implementations of the Driver interface. Since they are often created dynamically, you may want to preserve any implementations that you are processing as entry points:
-keep class * implements java.sql.Driver

This option also gets rid of the note that DexGuard prints out about (java.sql.Driver)Class.forName constructs, if you are instantiating a driver in your code (without necessarily implementing any drivers yourself).

Processing resource injection

If your application is using JEE-style resource injection, the application container will automatically assign instances of resource classes to fields and methods that are annotated with @Resource. The container applies introspection, even accessing private class members directly. It typically constructs a resource name based on the type name and the class member name. We then have to avoid that such class members are removed or renamed:
-keepclassmembers class * {
    @javax.annotation.Resource *;
}

The Spring framework has another similar annotation @Autowired:

-keepclassmembers class * {
    @org.springframework.beans.factory.annotation.Autowired *;
}

Processing resource files

If your application contains resource files, it may be necessary to adapt their names and/or their contents when the application is obfuscated. The following two options can achieve this automatically:
-adaptresourcefilenames    **.properties,**.gif,**.jpg
-adaptresourcefilecontents **.properties,META-INF/MANIFEST.MF

The -adaptresourcefilenames option in this case renames properties files and image files in the processed output, based on the obfuscated names of their corresponding class files (if any). The -adaptresourcefilecontents option looks for class names in properties files and in the manifest file, and replaces these names by the obfuscated names (if any). You'll probably want to adapt the filters to suit your application.

Processing manifest files

As illustrated in the previous section, manifest files can be treated like ordinary resource files. DexGuard can adapt obfuscated class names in the files, but it won't make any other changes. If you want anything else, you should apply an external tool. For instance, if a manifest file contains signing information, you should sign the jar again after it has been processed.

If you're merging several input jars into a single output jar, you'll have to pick one, typically by specifying filters:

-injars  in1.jar
-injars  in2.jar(!META-INF/MANIFEST.MF)
-injars  in3.jar(!META-INF/MANIFEST.MF)
-outjars out.jar

The filters will let DexGuard copy the manifest file from the first jar and ignore any manifest files in the second and third input jars. Note that DexGuard will leave the order of the files in the jars unchanged; manifest files are not necessarily put first.

Adding reflection for sensitive APIs

You can add reflection to obfuscate access to sensitive APIs, with the option -accessthroughreflection. For instance, this option replaces access to the standard cryptographic SecretKey class by equivalent reflective code:
-accessthroughreflection class javax.crypto.SecretKey {
    byte[] getEncoded();
}

For better obfuscation, you'll typically want to encrypt the resulting strings:

-encryptstrings "javax.crypto.SecretKey", "getEncoded"
or simply all strings in the class from which the cryptographic class is called:
-encryptstrings mypackage.MySecretClass

Finally, you can add another layer of obfuscation by also encrypting the latter class:

-encryptclasses mypackage.MySecretClass

Encrypting classes

You can encrypt entire classes by specifying them with the option -encryptclasses. It accepts a filter, which can contain wildcards and negators. For example:
-encryptclasses mypackage.MySecretClass, mypackage.MySecretClass$*
This option instructs to encrypt the specified class and all of its inner classes. The latter classes are easy to forget, because they are defined in the same source file, but they may be important too.

Encrypting strings

There are a few alternative ways to specify which constant strings in the code should be encrypted, with the option -encryptstrings. The shortest way is to specify the strings literally:
-encryptstrings "Some secret string", "Some other secret string"
In this case, the option has a filter, which can contain wildcards and negators.

Alternatively, you can specify final String fields, whose strings should be encrypted wherever they occur. For example:

-encryptstrings public class mypackage.MyConstants {
    public static final java.lang.String SECRET_KEY;
}

Alternatively, you can specify methods whose constant strings should all be encrypted. For example:

-encryptstrings public class mypackage.MySensitiveClass {
    public void mySecretMethod();
}

Finally, you can just specify classes whose constant strings should all be encrypted. For example:

-encryptstrings public class mypackage.MySecretClass
In these last three cases, the options have class specifications that specify one or more classes, fields, or methods. These specifications support multiple elements and various wildcards.

Producing useful obfuscated stack traces

tip The standard DexGuard build process already specifies this configuration for you.

These options let obfuscated applications or libraries produce stack traces that can still be deciphered later on:

-printmapping out.map

-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable

We're keeping all source file attributes, but we're replacing their values by the string "SourceFile". We could use any string. This string is already present in all class files, so it doesn't take up any extra space. If you're working with J++, you'll want to keep the "SourceDir" attribute as well.

We're also keeping the line number tables of all methods.

Whenever both of these attributes are present, the Java run-time environment will include line number information when printing out exception stack traces.

The information will only be useful if we can map the obfuscated names back to their original names, so we're saving the mapping to a file out.map. The information can then be used by the ReTrace tool to restore the original stack trace.

Obfuscating package names

tip The standard DexGuard build process already specifies this configuration for you.

Package names can be obfuscated in various ways, with increasing levels of obfuscation and compactness. For example, consider the following classes:

mycompany.myapplication.MyMain
mycompany.myapplication.Foo
mycompany.myapplication.Bar
mycompany.myapplication.extra.FirstExtra
mycompany.myapplication.extra.SecondExtra
mycompany.util.FirstUtil
mycompany.util.SecondUtil

Let's assume the class name mycompany.myapplication.MyMain is the main application class that is kept by the configuration. All other class names can be obfuscated.

By default, packages that contain classes that can't be renamed aren't renamed either, and the package hierarchy is preserved. This results in obfuscated class names like these:

mycompany.myapplication.MyMain
mycompany.myapplication.a
mycompany.myapplication.b
mycompany.myapplication.a.a
mycompany.myapplication.a.b
mycompany.a.a
mycompany.a.b

The -flattenpackagehierarchy option obfuscates the package names further, by flattening the package hierarchy of obfuscated packages:

-flattenpackagehierarchy 'myobfuscated'

The obfuscated class names then look as follows:

mycompany.myapplication.MyMain
mycompany.myapplication.a
mycompany.myapplication.b
myobfuscated.a.a
myobfuscated.a.b
myobfuscated.b.a
myobfuscated.b.b

Alternatively, the -repackageclasses option obfuscates the entire packaging, by combining obfuscated classes into a single package:

-repackageclasses 'myobfuscated'
The obfuscated class names then look as follows:
mycompany.myapplication.MyMain
mycompany.myapplication.a
mycompany.myapplication.b
myobfuscated.a
myobfuscated.b
myobfuscated.c
myobfuscated.d

Additionally specifying the -allowaccessmodification option allows access permissions of classes and class members to be broadened, opening up the opportunity to repackage all obfuscated classes:

-repackageclasses 'myobfuscated'
-allowaccessmodification
The obfuscated class names then look as follows:
mycompany.myapplication.MyMain
myobfuscated.a
myobfuscated.b
myobfuscated.c
myobfuscated.d
myobfuscated.e
myobfuscated.f

The specified target package can always be the root package. For instance:

-repackageclasses ''
-allowaccessmodification
The obfuscated class names are then the shortest possible names:
mycompany.myapplication.MyMain
a
b
c
d
e
f

Note that not all levels of obfuscation of package names may be acceptable for all code. Notably, you may have to take into account that your application may contain resource files that have to be adapted.

Publishing on the Samsung app market

If you're publishing your Android application on the Samsung app market, and you get "File upload has failed. There is no application ID(PackageName) or Version information(VersionName) in the Android Manifest.xml file you have registered.", then you need to preserve the mentioned resource XML attribute names in the AndroidManifest.xml:
-keepresourcexmlattributenames manifest/package,manifest/versionName 

The Samsung market parses these attributes when you upload your application, and they can't handle obfuscated names.

Other markets may try to read even more attributes. You could then refine your configuration experimentally, or just not obfuscate any attributes in the manifest at all:

-keepresourcexmlattributenames manifest/**

Removing logging code

You can let DexGuard remove logging code. The trick is to specify that the logging methods don't have side-effects — even though they actually do, since they write to the console or to a log file. DexGuard will take your word for it and remove the invocations (in the optimization step) and if possible the logging classes and methods themselves (in the shrinking step).

For example, this configuration removes invocations of the Android logging methods:

-assumenosideeffects class android.util.Log {
    public static boolean isLoggable(java.lang.String, int);
    public static int v(...);
    public static int i(...);
    public static int w(...);
    public static int d(...);
    public static int e(...);
}

The wildcards are a shortcut to match all versions of the methods.

Note that you generally can't remove logging code that uses System.out.println, since you would be removing all invocations of java.io.PrintStream#println, which could break your application. You can work around it by creating your own logging methods and let DexGuard remove those.

Restructuring the output archives

In simple applications, all output classes and resources files are merged into a single jar. For example:
-injars  classes
-injars  in1.jar
-injars  in2.jar
-injars  in3.jar
-outjars out.jar

This configuration merges the processed versions of the files in the classes directory and the three jars into a single output jar out.jar.

If you want to preserve the structure of your input jars (and/or wars, ears, zips, apks, or directories), you can specify an output directory (or a war, an ear, a zip, or an apk). For example:

-injars  in1.jar
-injars  in2.jar
-injars  in3.jar
-outjars out

The input jars will then be reconstructed in the directory out, with their original names.

You can also combine archives into higher level archives. For example:

-injars  in1.jar
-injars  in2.jar
-injars  in3.jar
-outjars out.war

The other way around, you can flatten the archives inside higher level archives into simple archives:

-injars  in.war
-outjars out.jar

This configuration puts the processed contents of all jars inside in.war (plus any other contents of in.war) into out.jar.

If you want to combine input jars (and/or wars, ears, zips, apks, or directories) into output jars (and/or wars, ears, zips, apks, or directories), you can group the -injars and -outjars options. For example:

-injars base_in1.jar
-injars base_in2.jar
-injars base_in3.jar
-outjars base_out.jar

-injars  extra_in.jar
-outjars extra_out.jar

This configuration puts the processed results of all base_in*.jar jars into base_out.jar, and the processed results of the extra_in.jar into extra_out.jar. Note that only the order of the options matters; the additional whitespace is just for clarity.

This grouping, archiving, and flattening can be arbitrarily complex. DexGuard always tries to package output archives in a sensible way, reconstructing the input entries as much as required.

Filtering the input and the output

tip The standard DexGuard build process already specifies this configuration for you.

If you want even greater control, you can add filters to the input and the output, filtering out zips, ears, wars, jars, apks, and/or ordinary files. For example, if you want to disregard certain files from an input jar:

-injars  in.jar(!images/**)
-outjars out.jar

This configuration removes any files in the images directory and its subdirectories.

Such filters can be convenient for avoiding warnings about duplicate files in the output. For example, only keeping the manifest file from a first input jar:

-injars  in1.jar
-injars  in2.jar(!META-INF/MANIFEST.MF)
-injars  in3.jar(!META-INF/MANIFEST.MF)
-outjars out.jar

It is also possible to filter the jars (and/or wars, ears, zips, apks) themselves, based on their names. For example:

-injars  in(**/acme_*.jar;)
-outjars out.jar

Note the semi-colon in the filter; the filter in front of it applies to jar names. In this case, only acme_*.jar jars are read from the directory in and its subdirectories. Filters for war names, ear names, and zip names can be prefixed with additional semi-colons. All types of filters can be combined. They are orthogonal.

On the other hand, you can also filter the output, in order to control what content goes where. For example:

-injars  in.jar
-outjars code_out.jar(**.class)
-outjars resources_out.jar

This configuration splits the processed output, sending **.class files to code_out.jar, and all remaining files to resources_out.jar.

Again, the filtering can be arbitrarily complex, especially when combined with grouping input and output.

Processing multiple applications at once

You can process several dependent or independent applications in one go, in order to save time and effort. DexGuard's input and output handling offers various ways to keep the output nicely structured.

The easiest way is to specify your input jars (and/or wars, ears, zips, apks, and directories) and a single output directory. DexGuard will then reconstruct the input in this directory, using the original jar names. For example, showing just the input and output options:

-injars  application1.jar
-injars  application2.jar
-injars  application3.jar
-outjars processed_applications

After processing, the directory processed_applications will contain processed versions of application jars, with their original names.

Finding dead code

These options list unused classes, fields, and methods in the application mypackage.MyApplication:
-injars      in.jar
-libraryjars /usr/local/android-sdk/platforms/android-15/android.jar

-dontoptimize
-dontobfuscate
-dontpreverify
-printusage

-keep public class mypackage.MyApplication {
    public static void main(java.lang.String[]);
}

We're not specifying an output jar, just printing out some results. We're saving some processing time by skipping the other processing steps.

The java compiler inlines primitive constants and String constants (static final fields). DexGuard would therefore list such fields as not being used in the class files that it analyzes, even if they are used in the source files. We can add a -keepclassmembers option that keeps those fields a priori, in order to avoid having them listed:

-keepclassmembers class * {
    static final %                *;
    static final java.lang.String *;
}

Using annotations to configure DexGuard

The traditional DexGuard configuration allows to keep a clean separation between the code and the configuration for shrinking, optimization, and obfuscation. However, it is also possible to define specific annotations, and then annotate the code to configure the processing.

You can find a set of such predefined annotations in the directory examples/annotations/lib in the DexGuard distribution. The annotation classes are defined in annotations.jar. The corresponding DexGuard configuration (or meta-configuration, if you prefer) is specified in annotations.pro. With these files, you can start annotating your code. For instance, a java source file Application.java can be annotated as follows:

@KeepApplication
public class Application {
  ....
}

The DexGuard configuration file for the application can then be simplified by leveraging off these annotations:

-injars      in.jar
-outjars     out.jar
-libraryjars /usr/local/android-sdk/platforms/android-15/android.jar

-include lib/annotations.pro

The annotations are effectively replacing the application-dependent -keep options. You may still wish to add traditional -keep options for processing native methods, enumerations, serializable classes, and annotations.

The directory examples/annotations contains more examples that illustrate some of the possibilities.


Copyright © 2002-2014 Saikoa BVBA.