我在发布Android应用程序时包含this ProGuard configuration以删除调试日志语句:
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** v(...);
}
这可以按预期工作 - 我可以从ProGuard日志和Android日志输出中看到,Log.d("This is a debug statement");
之类的调用已被删除。
但是,如果我在此阶段对应用程序进行反编译,我仍然可以看到所有使用的String
文字 - 例如本例中的This is a debug statement
。
有没有办法同时删除字节码中不再需要的每个String
?
答案 0 :(得分:46)
ProGuard可以删除简单的常量参数(字符串,整数等)。所以在这种情况下,代码和字符串常量应该完全消失:
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需要转义分析和一些其他技术,但尚未在此版本中使用。
至于解决方案:您可以创建其他带有简单参数的调试方法,并让ProGuard删除它们:
MyLog.d("The answer is ", answer);
或者,您可以尝试为每个调试语句添加一个条件,以便ProGuard稍后可以将其评估为false。这个选项可能有点复杂,在调试标志的初始化方法上需要一些额外的-assumenosideeffects选项。
答案 1 :(得分:6)
这是我们如何做到的 - 使用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>
答案 2 :(得分:5)
由于我没有足够的代表来直接评论蚂蚁任务答案,所以这里有一些更正,因为它证明与像詹金斯这样可以执行发布版本的CI服务器组合非常有用: / p>
<target name="removelogs">
<replaceregexp byline="true">
<regexp pattern="\s*Log\.d\s*\(.*\)\s*;"/>
<substitution expression="{};"/>
<fileset dir="src">
<include name="**/*.java"/>
</fileset>
</replaceregexp>
</target>
'。' Log必须转义后才能转义为'。'括号内部指向任何日志记录语句,而不仅仅是'\ s *'的空格。
由于我对RegEx没有多少经验,我希望这会帮助处于相同情况的某些人使这个ant任务工作(例如在Jenkins上)。
答案 3 :(得分:0)
如果您想支持多行日志调用,可以改为使用此正则表达式:
(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>
注意:这围绕着使用if(false){
和}
的日志调用,因此原始调用将被保留,以供参考和在检查中间构建文件时保留行号,让java编译器剥离编译期间调用。
如果您希望完全删除日志调用,可以这样做:
<replaceregexp>
<regexp pattern="(android\.util\.)*Log\.([ewidv]|wtf)\s*\([\S\s]*?\)\s*;"/>
<substitution expression=""/>
<fileset dir="src/">
<include name="**/*.java"/>
</fileset>
</replaceregexp>
您还可以将正则表达式应用为<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>