2011-08-17 206 views
20

可能重复的字符串:
Removing unused strings during ProGuard optimisation使用ProGuard删除记录不会被删除记录

我有几十个记录语句的Android应用程序。我宁愿他们将不会出现在发行版本,所以我用Proguard的像这样的东西在proguard.cfg文件:

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

但问题是,有很多的Log.d("something is " + something),虽然Log.d()语句正在从字节码中删除,字符串仍然存在

所以,以下this答案,我创建了一个简单的包装类,沿着线的东西:

public class MyLogger { 
    public static void d(Object... msgs) { 
     StringBuilder log = new StringBuilder(); 
     for(Object msg : msgs) { 
      log.append(msg.toString()); 
     } 
     Log.d(TAG, log.toString()); 
    } 
} 

然后我编辑proguard.cfg

-assumenosideeffects class my.package.MyLogger { 
    public static *** d(...); 
} 

但字符串仍然被找到在生成的字节码中!

除此之外,我使用Android SDK提供的标准proguard.cfg。难道我做错了什么?


编辑:检查生成的字节码之后,我看到了弦在那里,但他们没有被追加另一个人,因为我以为。他们正在存储在一个阵列。为什么?将它们作为可变参数传递给我的方法。它看起来像ProGuard的不喜欢,所以我修改了我的记录器类是这样的:

public static void d(Object a) { 
    log(a); 
} 

public static void d(Object a, Object b) { 
    log(a, b); 
} 

(... I had to put like seven d() methods ...) 

private static void log(Object... msgs) { 
    (same as before) 
} 

这是丑陋的,但现在的字符串是无处字节码。

这是ProGuard的某种bug /限制吗?还是只是我不理解它是如何工作的?

+0

你可能想避免proguard黑客,只是使用一个常量:http://stackoverflow.com/questions/4199563/android-util-log-when-publishing-what-c​​an-i-do-not-do如果然后你使用一个常量proguard会在它==假时删除代码行。 – Blundell

+2

的确,这是一个黑客攻击,也是一个丑陋的攻击,但我认为这比将if(DEBUG)放在任何地方都好。生成的代码更清晰,结果在导出的应用程序中是相同的。另外,出于某种原因,如果调试常量是错误的,Eclipse会对死代码抱怨,我不喜欢这样。感谢您的想法,但:) –

+0

请注意,如果您传递基本数据类型,可能仍然有一些死代码。 Proguard不会将基本数据类型移除到相应的“对象”转换。例如。如果你传递一个'int',Proguard将会在'Integer.valueOf(someVar)'行中离开;' – crazymaik

回答

11

对于不同数量的参数,您使用不同方法的解决方案可能是最方便的。具有可变数量参数的单个方法会更好,但是当前版本的ProGuard不够聪明,无法删除所有未使用的代码。

java编译器将可变数量的参数编译为单个数组参数。在调用方面,这意味着创建一个合适大小的数组,填充元素并将其传递给方法。 ProGuard认为该数组正在被创建,然后被用于存储元素。它并没有意识到(然而)它并没有用于实际有用的操作,方法调用消失了。结果,数组的创建和初始化被保留。

如果您希望进行某些特定的优化,那么检查已处理的代码是一种很好的做法。

+1

我刚开始使用职业守卫去掉日志并面临同样的死代码问题。我想知道为什么职业后卫无法在随后的传球中移除这些阵列。我见过IDEs指出一个局部变量从来没有使用过。所以,这似乎是可能的。 –

4

Proguard的不工作我也一样,我不喜欢创建一个围绕日志的包装,所以我测试了以下解决方案:

工作液

final static boolean IsDebugging = false; 

并在代码:

if(IsDebugging) 
    Log.i(TAG, "Debugging"); 

认为你必须使用最终关键字,这样ISDE在编译时窃听成为真或假,并且日志字符串不会出现在最终的字节码中。

不工作(IsDebugMode)

public static boolean IsDebugMode(Context context) { 
    PackageManager pm = context.getPackageManager(); 
    PackageInfo pi; 
    try { 
     pi = pm.getPackageInfo(context.getPackageName(),0); 
    } catch (NameNotFoundException e) { 
     return false; 
    } 
    return (pi.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; 
} 

,并在我的代码:

boolean IsDebugging = Utils.IsDebugMode(this); 

    if(IsDebugging) 
     Log.i(TAG, "Debugging"); 

上述解决方案不打印logcat的任何日志,但日志字符串被编译并存在最后的字节码是坏的。

另一解决方案是:

不工作(BuildConfid.DEBUG)

if(BuildConfig.DEBUG) 
     Log.i(TAG, "Debugging"); 

这是相同IsDebugMode溶液。它不打印任何内容,但字符串在最终的字节码中。又坏了!

+0

请参阅我的问题的第二个评论。 –