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