使用ProGuard删除日志记录不会删除记录的字符串

MM.*_*MM. 20 android proguard

可能重复:
在ProGuard优化期间删除未使用的字符串

我有一个包含许多日志语句的Android应用程序.我希望它们不会出现在发布版本中,所以我在proguard.cfg文件中使用了Proguard这样的东西:

-assumenosideeffects class android.util.Log {
    public static *** d(...);
}
Run Code Online (Sandbox Code Playgroud)

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

所以,按照这个答案,我创建了一个简单的包装类,类似于:

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());
    }
}
Run Code Online (Sandbox Code Playgroud)

然后我编辑了我proguard.cfg:

-assumenosideeffects class my.package.MyLogger {
    public static *** d(...);
}
Run Code Online (Sandbox Code Playgroud)

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

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


编辑:在检查生成的字节码之后,我看到字符串在那里,但它们并没有像我想的那样相互追加.它们存储在一个数组中.为什么?将它们作为变量参数传递给我的方法.看起来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)
}
Run Code Online (Sandbox Code Playgroud)

这很难看,但现在字符串中没有字符串.

这是ProGuard的某种错误/限制吗?或者只是我不理解它是如何工作的?

Eri*_*une 11

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

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

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


Moh*_*hin 5

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

工作解决方案

final static boolean IsDebugging = false;
Run Code Online (Sandbox Code Playgroud)

并在您的代码中:

if(IsDebugging)
   Log.i(TAG, "Debugging");
Run Code Online (Sandbox Code Playgroud)

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

不工作(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;
}
Run Code Online (Sandbox Code Playgroud)

在我的代码中:

    boolean IsDebugging = Utils.IsDebugMode(this);

    if(IsDebugging)
      Log.i(TAG, "Debugging");
Run Code Online (Sandbox Code Playgroud)

上述解决方案不会在 Logcat 中打印任何日志,但日志字符串已编译并存在于最终字节码中,这是错误的。

另一个解决方案是:

不工作(BuildConfid.DEBUG)

    if(BuildConfig.DEBUG)
      Log.i(TAG, "Debugging");
Run Code Online (Sandbox Code Playgroud)

这与 IsDebugMode 解决方案相同。它不打印任何内容,但字符串在最终字节码中。又坏了!