在ProGuard优化过程中删除未使用的string

当我发布一个Android应用程序时,我将这个ProGuardconfiguration包含在去除debugging日志语句中:

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

这个按预期工作 – 我可以从ProGuard日志和Android日志输出中看到诸如Log.d("This is a debug statement");调用Log.d("This is a debug statement"); 被删除。

不过,如果我在这个阶段反编译应用程序,我仍然可以看到所有使用的String字面值,即在这个例子中This is a debug statement

有没有办法也删除字节码中不再需要的每个String

ProGuard可以删除简单的常量参数(string,整数等)。 所以在这种情况下,代码和string常量应该完全消失:

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

但是,您可能已经使用如下代码观察了该问题:

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

编译之后,这实际上对应于:

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

ProGuard 4.6版本可以将其简化为:

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

所以伐木已经结束了,但是最优化的步骤还是留下了一些毛病。 如果没有关于StringBuilder类的更深入的知识,简化这个过程就非常棘手。 就ProGuard而言,可能会说:

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

对于一个人来说,StringBuilder代码显然可以被删除,但是DatabaseBuilder代码可能不能。 ProGuard需要转义分析和一些其他技术,这些还没有在这个版本中。

至于解决scheme:您可以创build额外的debugging方法,使用简单的参数,并让ProGuard删除这些:

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

或者,您可以尝试在每个debugging语句前添加ProGuard稍后可能评估为false的条件。 这个选项可能有点复杂,需要在debugging标志的初始化方法上增加一些额外的-assumenosideeffects选项。

这里是我们如何做 – 使用ant任务

 <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> 

由于我没有足够的代表来直接评论ant任务答案,所以在这里对它进行了一些修正,因为它certificate了和Jenkins这样的可以在发布版本中执行它的CI服务器的组合非常有帮助:

 <target name="removelogs"> <replaceregexp byline="true"> <regexp pattern="\s*Log\.d\s*\(.*\)\s*;"/> <substitution expression="{};"/> <fileset dir="src"> <include name="**/*.java"/> </fileset> </replaceregexp> </target> 

'。' 日志必须转义后,一个'。' 括号内的目标是任何日志语句,而不仅仅是'\ s *'的空格。

由于我对RegEx没有太多的经验,我希望这能帮助一些处于同一情况下的人获得这个ant任务(例如Jenkins)。

如果你想支持多行日志调用,你可以使用这个正则expression式代替:

 (android\.util\.)*Log\.@([ewidv]|wtf)\s*\([\S\s]*?\)\s*; 

你应该可以像这样在一个ant replaceregexp任务中使用它:

 <replaceregexp> <regexp pattern="((android\.util\.)*Log\.([ewidv]|wtf)\s*\([\S\s]*?\)\s*;)"/> <substitution expression="if(false){\1}"/> <fileset dir="src/"> <include name="**/*.java"/> </fileset> </replaceregexp> 

注意:这将围绕Log调用,使用if(false){}来保留原始调用,以便在检查中间构build文件时进行引用和保留行号,让java编译器在编译过程中去除调用。

如果您希望完全删除日志调用,则可以这样做:

 <replaceregexp> <regexp pattern="(android\.util\.)*Log\.([ewidv]|wtf)\s*\([\S\s]*?\)\s*;"/> <substitution expression=""/> <fileset dir="src/"> <include name="**/*.java"/> </fileset> </replaceregexp> 

您也可以将正则expression式作为filter应用于<copy>任务中,如下所示:

 <copy ...> <fileset ... /> <filterchain> <tokenfilter if:true="${strip.log.calls}"> <stringtokenizer delims=";" includeDelims="true"/> <replaceregex pattern="((android\.util\.)*Log\.([ewidv]|wtf)\s*\([\S\s]*?\)\s*;)" replace="if(false){\1}"/> </tokenfilter> </filterchain> <!-- other-filters-etc --> </copy>