Удаление неиспользуемых строк во время оптимизации ProGuard - PullRequest
29 голосов
/ 15 мая 2011

Я включаю эту конфигурацию ProGuard , чтобы исключить операторы журнала отладки при выпуске приложения для Android:

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

Это работает как ожидалось & mdash; Из журналов ProGuard и вывода журнала Android видно, что такие вызовы, как Log.d("This is a debug statement");, удалены.

Однако, если я декомпилирую приложение на этом этапе, я все равно смогу увидеть все использованные литералы String & mdash; т.е. This is a debug statement в этом примере.

Есть ли способ также удалить каждый String, который больше не нужен из байт-кода?

Ответы [ 4 ]

46 голосов
/ 17 мая 2011

ProGuard может удалять простые постоянные аргументы (строки, целые числа и т. Д.). Так что в этом случае код и строковая константа должны полностью исчезнуть:

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 требует анализа побега и нескольких других методов, которых пока нет в этой версии.

Что касается решения: вы можете создать дополнительные методы отладки, которые принимают простые аргументы, и позволить ProGuard удалить их:

MyLog.d("The answer is ", answer);

В качестве альтернативы, вы можете попробовать поставить перед каждым отладочным оператором префикс с условием, которое ProGuard может позже оценить как ложное. Эта опция может быть немного более запутанной, требуя некоторой дополнительной опции -assumenosideeffects для метода инициализации для флага отладки.

6 голосов
/ 16 мая 2011

вот как мы это делаем - используя задачу муравья

<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>
5 голосов
/ 12 сентября 2012

Поскольку у меня недостаточно представителя, чтобы комментировать ответ на задачу ant, вот некоторые исправления для него, поскольку он оказывается очень полезным в сочетании с CI-сервером, таким как Jenkins, который может выполнить его для сборки выпуска:

<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, я надеюсь, что это поможет некоторым людям в той же ситуации получить этоМуравьиная задача работает (например, на Дженкинс).

0 голосов
/ 17 ноября 2016

Если вы хотите поддерживать многострочные вызовы журнала, вместо этого вы можете использовать это регулярное выражение:

(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){ и }, поэтому исходный вызов сохраняется как для справки, так и для сохранения номеров строк при проверке промежуточных файлов сборки, позволяя компилятору java обрезать вызовы во время компиляции.

Если вы предпочитаете полностью удалить вызовы журнала, вы можете сделать это следующим образом:

<replaceregexp>
    <regexp pattern="(android\.util\.)*Log\.([ewidv]|wtf)\s*\([\S\s]*?\)\s*;"/>
    <substitution expression=""/>
    <fileset dir="src/">
        <include name="**/*.java"/>
    </fileset>
</replaceregexp>

Вы также можете применить регулярное выражение в качестве фильтра в задаче <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>
...