在ProGuard优化期间删除未使用的字符串

时间:2011-05-15 14:43:47

标签: android proguard

我在发布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

4 个答案:

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