Подтвердить что ты не робот

Удаление записи с помощью ProGuard не удаляет строки, которые записываются в журнал

Возможный дубликат:
Удаление неиспользуемых строк во время оптимизации 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(...);
}

Но строки все еще находятся в сгенерированном байт-коде!

Кроме этого, я использую стандартный 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)
}

Это уродливо, но теперь строки нигде в байт-коде.

Это какая-то ошибка/ограничение ProGuard? Или я просто не понимаю, как это работает?

4b9b3361

Ответ 1

Ваше решение с различными методами для разного количества аргументов, вероятно, является наиболее удобным. Единственный метод с переменным числом аргументов будет приятнее, но текущая версия ProGuard недостаточно умен, чтобы удалить все неиспользованный код.

Компилятор java компилирует переменное количество аргументов как один аргумент массива. На стороне вызова это означает создание массива нужного размера, заполнение элементов и передачу его методу. ProGuard видит, что массив создается, а затем используется для хранения в нем элементов. Он не понимает (пока), что он тогда не используется для реальных полезных операций с исчезновением метода. В результате сохранение и инициализация массива сохраняются.

Хорошая практика проверки обработанного кода, если вы ожидаете определенной оптимизации.

Ответ 2

Proguard тоже не работает, и мне не нравится создавать обертку вокруг журнала, поэтому я протестировал следующие решения:

РАБОЧЕЕ РЕШЕНИЕ

final static boolean IsDebugging = false;

и в вашем коде:

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

считают, что вы должны использовать ключевое слово 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;
}

и в моем коде:

    boolean IsDebugging = Utils.IsDebugMode(this);

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

Вышеупомянутое решение не печатает Log Log, но строка журнала скомпилирована и существует в последнем байт-коде, который является плохим.

Другое решение:

НЕ РАБОТАЕТ (BuildConfid.DEBUG)

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

Это то же самое, что и решение IsDebugMode. Он ничего не печатает, но строки находятся в последнем байт-коде. Плохо снова!