在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时使用jackson-json库的问题
  • 错误Proguard不支持的类版本号
  • 外部jar的Proguard错误 - 返回错误代码1
  • Android&Proguard - 如何混淆,但不优化任何代码?
  • dagger android支持proguard规则
  • ProGuard错误无法find超类或接口org.apache.http.entity
  • Retrofit2 proguard删除参数
  • 如何从proguard版本中排除R * .class文件
  • 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>