【问题标题】:Removing logging with ProGuard doesn't remove the strings being logged [duplicate]使用 ProGuard 删除日志不会删除正在记录的字符串 [重复]
【发布时间】:2011-10-28 13:15:00
【问题描述】:

可能重复:
Removing unused strings during ProGuard optimisation

我有一个包含数十条日志记录语句的 Android 应用程序。我希望它们不会出现在发布版本中,所以我在 proguard.cfg 文件中使用了 Proguard 和类似的东西:

-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 不喜欢这样,所以我修改了我的 logger 类:

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 的某种错误/限制吗?还是只是我不明白它是如何工作的?

【问题讨论】:

  • 你可能想避免 proguard hack,只使用一个常量:stackoverflow.com/questions/4199563/… 如果你使用常量,proguard 将在它 == false 时删除代码行。
  • 这确实是一个 hack,而且是一个丑陋的,但我认为这比把if(DEBUG) 到处都好。生成的代码更清晰,并且在导出的应用程序中结果相同。此外,由于某种原因,如果调试常量为假,Eclipse 会抱怨死代码,我不喜欢这样。不过谢谢你的想法:)
  • 请注意,如果您传递基本数据类型,可能仍然会有一些死代码。 Proguard 不会删除基本数据类型以进行相应的Object 转换。例如。如果你传递了int,Proguard 将离开Integer.valueOf(someVar);

标签: android proguard


【解决方案1】:

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

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

如果您期望某些特定的优化,最好检查已处理的代码。

【讨论】:

  • 我刚刚开始使用 pro Guard 来删除日志并且面临同样的死代码问题。我想知道为什么 pro Guard 无法在后续传递中删除这些数组。我见过 IDE 指出从未使用过局部变量。所以,这似乎是可能的。
【解决方案2】:

Proguard 对我也不起作用,而且我不喜欢围绕 Log 创建包装器,因此我测试了以下解决方案:

可行的解决方案

final static boolean IsDebugging = false;

在你的代码中:

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

请考虑您必须使用 final 关键字,以便 IsDebugging 在编译时变为 true 或 false,并且日志字符串不会出现在最终字节码中。

不工作(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 解决方案相同。它不打印任何内容,但字符串在最终字节码中。又坏了!

【讨论】:

  • 请看我的问题的第二条评论。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-01-26
  • 2012-10-24
  • 1970-01-01
相关资源
最近更新 更多