Removing Unused Strings During Proguard Optimisation

Removing unused strings during ProGuard optimisation

ProGuard can remove simple constant arguments (Strings, integers, etc). So in this case, the code and the string constant should disappear completely:

Log.d("This is a debug statement");

However, you may have observed the issue with some code like this:

Log.d("The answer is "+answer);

After compilation, this actually corresponds to:

Log.d(new StringBuilder().append("The answer is ").append(answer).toString());

ProGuard version 4.6 can simplify this to something like:

new StringBuilder().append("The answer is ").append(answer).toString();

So the logging is gone, but the optimization step still leaves some fluff behind. It's surprisingly tricky to simplify this without some deeper knowledge about the StringBuilder class. As far as ProGuard is concerned, it could say:

new DatabaseBuilder().setup("MyDatabase").initialize(table).close();

For a human, the StringBuilder code can obviously be removed, but the DatabaseBuilder code probably can't. ProGuard requires escape analysis and a few other techniques, which aren't in this version yet.

As for a solution: you can create additional debug methods that take simple arguments, and let ProGuard remove those:

MyLog.d("The answer is ", answer);

Alternatively, you can try prefixing every debug statement with a condition that ProGuard can later evaluate as false. This option may be a bit more convoluted, requiring some additional -assumenosideeffects option on an initialization method for the debug flag.

Removing logging with ProGuard doesn't remove the strings being logged

Your solution with different methods for different numbers of arguments is probably the most convenient one. A single method with a variable number of arguments would be nicer, but the current version of ProGuard is not smart enough to remove all the unused code.

The java compiler compiles a variable number of arguments as a single array argument. On the invocation side, this means creating an array of the right size, filling out the elements, and passing it to the method. ProGuard sees that the array is being created and then being used to store elements in it. It doesn't realize (yet) that it then isn't used for actual useful operations, with the method invocation being gone. As a result, the array creation and initialization are preserved.

It's a good practice to check the processed code if you're expecting some particular optimization.

Preventing ProGuard from evaluating mathematical expressions and removing unused instructions in Android

I just checked by compiling this class without proguard

public class Foo {

final Integer bar = new Integer(Integer.MIN_VALUE+Integer.MAX_VALUE+1);

}

and the compile constructor was

  public <init>()V
L0
LINENUMBER 1 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 3 L1
ALOAD 0
NEW java/lang/Integer
DUP
ICONST_0 // <--- evaluated expression result
INVOKESPECIAL java/lang/Integer.<init> (I)V
PUTFIELD Foo.bar : Ljava/lang/Integer;
RETURN
L2
LOCALVARIABLE this LFoo; L0 L2 0
MAXSTACK = 4
MAXLOCALS = 1

So that it is not proguard that is doing it but the Java compiler, this is happening because the value in the constructor of Integer is a constant expression and is evaluated at compile time for optimization.

You could check, for instance, that compiling this other version of the class

public class Foo {

final Integer bar = new Integer(getA()+getB()+1);

private int getA(){
return Integer.MIN_VALUE;
}

private int getB(){
return Integer.MAX_VALUE;
}
}

will result in a different constructor

  public <init>()V
L0
LINENUMBER 1 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
L1
LINENUMBER 3 L1
ALOAD 0
NEW java/lang/Integer
DUP
ALOAD 0
INVOKESPECIAL Foo.getA ()I
ALOAD 0
INVOKESPECIAL Foo.getB ()I
IADD
ICONST_1
IADD
INVOKESPECIAL java/lang/Integer.<init> (I)V
PUTFIELD Foo.bar : Ljava/lang/Integer;
RETURN
L2
LOCALVARIABLE this LFoo; L0 L2 0
MAXSTACK = 5
MAXLOCALS = 1

As you can see, in this second version the 2 additions are actually performed because the values are obtained from a method invocation and therefore are not constant anymore.

What am i doing wrong in ProGuard configuration to remove log lines?

You can only remove logging if optimization is not disabled, which requires specifying a different global configuration file in project.properties:

  proguard.config=${sdk.dir}/tools/proguard/proguard-android-optimize.txt:proguard-project.txt

Similar questions and answers:

  • Removing Log call using proguard
  • Removing unused strings during ProGuard optimisation
  • Removing logging with ProGuard doesn't remove the strings being logged
  • How to config my proguard-project.txt file to remove just Logs
  • Android Proguard Options - Stricter Rules & Remove Log Statements

Best way to configure Proguard to strip out unused classes from Restlet library

First I used the -dontobfuscate flag to run Proguard without obfuscation. My exception still happened, but it was easier to see the cause:

10-20 08:43:06.725  4475  4499 W System.err: Error while handling an HTTP client call
10-20 08:43:06.725 4475 4499 W System.err: java.lang.NoSuchMethodException: ClientAdapter(Context)
10-20 08:43:06.725 4475 4499 W System.err: at java.lang.Class.getMatchingConstructor(Class.java:643)
10-20 08:43:06.725 4475 4499 W System.err: at java.lang.Class.getConstructor(Class.java:472)
10-20 08:43:06.725 4475 4499 W System.err: at org.restlet.engine.adapter.HttpClientHelper.getAdapter(HttpClientHelper.java:100)
10-20 08:43:06.725 4475 4499 W System.err: at org.restlet.engine.adapter.HttpClientHelper.handle(HttpClientHelper.java:111)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.Client.handle(Client.java:180)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.routing.Filter.doHandle(Filter.java:159)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.routing.Filter.handle(Filter.java:206)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.resource.ClientResource.handle(ClientResource.java:1137)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.resource.ClientResource.handleOutbound(ClientResource.java:1226)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.resource.ClientResource.handle(ClientResource.java:1069)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.resource.ClientResource.handle(ClientResource.java:1045)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.resource.ClientResource.post(ClientResource.java:1454)
10-20 08:43:06.735 4475 4499 W System.err: at org.restlet.resource.ClientResource.post(ClientResource.java:1400)

I used JD-GUI to open the bin/proguard/obfuscated.jar and confirmed that the constructor had been removed. The missing constructor was in the Adapter super class.

public Adapter(Context context) {
this.context = context;
}

The fix for that specific problem was to add this Proguard rule:

-keep class org.restlet.engine.adapter.Adapter { *; }

Rebuild and testing with a simple HTTP connect revealed more rules I needed.

Using the rules below allows me to make successful HTTP and HTTPS connections.

# Preserve a minimal number of Restlet classes.  It is particularly
# difficult to get these keep rules correct as Restlet uses quite a
# lot of reflection.
-keep class org.restlet.engine.log.LoggerFacade
-keep class org.restlet.Application
-keep class org.restlet.Client
-keep class org.restlet.Context
-keep class org.restlet.Connector
-keep class org.restlet.Request
-keep class org.restlet.Response
-keep class org.restlet.Restlet
-keep class org.restlet.data.Status
-keep class org.restlet.engine.adapter.Adapter { *; }
-keep class org.restlet.engine.adapter.ClientCall
-keep class org.restlet.engine.adapter.ClientAdapter { *; }
-keep class org.restlet.engine.adapter.HttpClientHelper { *; }
-keep class org.restlet.engine.connector.Connection
-keep class org.restlet.engine.ClientHelper { *; }
-keep class org.restlet.engine.header.Header { *; }

-keep class org.restlet.* extends org.restlet.Client { *; }
-keep class org.restlet.* extends org.restlet.Context { *; }
-keep class org.restlet.* extends org.restlet.Connector { *; }
-keep class org.restlet.* extends org.restlet.engine.ClientHelper { *; }
-keep class org.restlet.* extends org.restlet.resource.Resource { *; }

# We use constants like Disposition.NAME_FILENAME
-keepclassmembers class org.restlet.data.Disposition { public static final *; }

# Ignore these Restlet warnings, as we have never included the
# org.jsslutils.* classes in our build
-dontwarn org.restlet.ext.ssl.**

These rules might not work for you if you use the Restlet library differently to me, but they might be a useful starting point.

Use Proguard for stripping unused Support lib classes

The Android SDK only applies ProGuard in release builds, not in debug builds.

Furthermore, the Android SDK (r20 or higher) typically looks for proguard-project.txt instead of proguard.txt in your project. This file can generally be empty, because the build process also reads the global file proguard-android.txt. You may want to update your project with

android update project -p MyProjectDirectory

Finally, the Android SDK (r20 or higher) disables ProGuard's optimization step by default (which can improve on its shrinking step). You can enable it by pointing to proguard-android-optimize.txt instead of proguard-android.txt in your project.properties.



Related Topics



Leave a reply



Submit