Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Removing unused strings during ProGuard optimisation

I include this ProGuard configuration to strip out debug log statements when I release an Android application:

-assumenosideeffects class android.util.Log {     public static *** d(...);     public static *** v(...); } 

This works as expected — I can see from the ProGuard logs and Android log output that calls such as Log.d("This is a debug statement"); are removed.

However, if I decompile the app at this stage, I can still see all the String literals that were used — i.e. This is a debug statement in this example.

Is there a way to also remove each String that's no longer needed from the bytecode?

like image 419
Christopher Orr Avatar asked May 15 '11 14:05

Christopher Orr


People also ask

Does ProGuard remove unused code?

ProGuard Facts:It removes unused codes from your libraries as well. (Which means, any method or field that is not referenced anywhere will be removed by ProGuard Shrinker). Helps to reduce the build size by obfuscating the classes, methods, & fields with short names.

What is difference between R8 and ProGuard?

R8 has more Kotlin support than that of Proguard. R8 is having a faster processing time than Proguard which reduces build time. R8 gives better output results than Proguard. R8 reduces the app size by 10 % whereas Proguard reduces app size by 8.5 %.

What is ProGuard obfuscation?

ProGuard is a command-line tool that reduces app size by shrinking bytecode and obfuscates the names of classes, fields and methods. It's an ideal fit for developers working with Java or Kotlin who are primarily interested in an Android optimizer.

Where is ProGuard Android optimize txt?

The getDefaultProguardFile('proguard-android. txt') method obtains the default ProGuard settings from the Android SDK tools/proguard/ folder. The proguard-android-optimize. txt file is also available in this Android SDK folder with the same rules but with optimizations enabled.


2 Answers

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.

like image 73
Eric Lafortune Avatar answered Sep 28 '22 15:09

Eric Lafortune


here is how we do it - using ant task

<target name="base.removelogs">     <replaceregexp byline="true">         <regexp pattern="Log.d\s*\(\s*\)\s*;"/>         <substitution expression="{};"/>         <fileset dir="src/"><include name="**/*.java"/></fileset>     </replaceregexp> </target> 
like image 23
nir Avatar answered Sep 28 '22 14:09

nir