Как уменьшить код Android с помощью Proguard

Поскольку я использую много зависимостей в своем приложении, я достиг предела методов 65k (я достигаю 76k методов). Я читал на android.developer, что proguard используется для сжатия кода.

Итак, proguard сокращает только код моего приложения или также сокращает код моих зависимостей? Нужно ли мне опасаться чего-то при сжатии кода с помощью proguard? Как я могу это сделать?

Моя сборка Gradle:

apply plugin: 'com.android.application'

android {
compileSdkVersion 21
buildToolsVersion "21.1.2"

defaultConfig {
    applicationId "some.Path"
    minSdkVersion 15
    targetSdkVersion 21
    versionCode 1
    versionName "1.0"
}

packagingOptions {
    exclude 'META-INF/DEPENDENCIES'
    exclude 'META-INF/NOTICE'
    exclude 'META-INF/NOTICE.txt'
    exclude 'META-INF/LICENSE'
    exclude 'META-INF/LICENSE.txt'
}

buildTypes {
    release {
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
    debug {
        debuggable true
        minifyEnabled true
        shrinkResources true
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
}
}

configurations {
compile.exclude group:  'org.apache.xmlbeans'
}

repositories {
maven { url "https://jitpack.io" }
}

dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:21.0.3'
compile 'com.github.PhilJay:MPAndroidChart:v2.1.0'
compile 'com.opencsv:opencsv:3.4'
compile 'org.apache.poi:poi:3.12'
compile 'org.apache.poi:poi-ooxml:3.12'
}

person pebbles    schedule 19.07.2015    source источник
comment
im reaching the 65k Methode Limit. Пожалуйста, опубликуйте свой build.gradle.   -  person Jared Burrows    schedule 20.07.2015


Ответы (6)


TL; ДР: инвертируйте свой вариант -keep, если вы любите неприятности


Во-первых: я считаю, что вы делаете правильный выбор, используя Proguard для преодоления ограничения dex. Я бы не рекомендовал использовать библиотеку поддержки multidex ни при каких обстоятельствах: это создает проблему нескольких загрузчиков классов в вашем приложении, и это может иметь неприятные последствия многими неочевидными способами.

Вот мой личный подход к эффективному сжатию приложения:

  • Выберите пару самых больших сторонних зависимостей, которые у вас есть;
  • Проверьте, действительно ли они поддерживают Proguard;
  • Если да, уменьшите их с помощью Proguard;
  • Если вы все еще не вписываетесь в максимальное количество методов, выполните шаги, описанные выше, для некоторых оставшихся зависимостей;
  • Если вы все еще не подходите, возможно, переоцените некоторые из них, которые не поддерживают Proguard, возможно, прочитайте их исходный код, чтобы лучше понять, почему они не поддерживают, и примените Proguard. к ним себя;
  • В худшем случае примените Proguard к своему коду;
  • Если абсолютно ничего из вышеперечисленного не помогает, используйте multidex.

Выбор зависимостей для сжатия

В вашем случае не так много (прямых) зависимостей. Вы можете просмотреть вывод gradlew dependencies, чтобы получить более полное представление о ваших непрямых зависимостях, некоторые из которых могут вносить наибольший вклад в общий размер приложения. Затем вы можете продолжить использовать некоторые из инструментов, перечисленных в разделе «Dex» Android Arsenal, чтобы узнать, какие библиотеки больше всего способствуют подсчету методов dex. Вы, кажется, уже имеете общее представление об этом, так что я не буду долго останавливаться на этой части.

Помните: сжатие исполняемого кода — это несколько нетривиальное вмешательство во внутренние процессы библиотеки, так что лучше сжимать меньше, чтобы избежать загадочных проблем в будущем. Если вы сомневаетесь, начните с библиотек, которые открыто заявляют, что они действительно официально поддерживают Proguard (в вашем случае это будут библиотеки поддержки Android).

Обратите внимание, что «поддержка Proguard» может означать разные вещи для разных разработчиков. Вы можете ожидать, что разработчики библиотеки поддержки Android будут, по крайней мере, в основном компетентными, но многие другие будут поставляться с потребительскими правилами Proguard, такими как это:

-keep class com.example.library.** { *; }

Если вам интересно, приведенная выше конфигурация основана на многих реальных конфигурациях, таких как Конфигурация Square's Leak Canary Proguard. В нем ничего не говорится об общей компетенции рассматриваемых разработчиков, просто напоминание о том, что использование Proguard может быть трудным. И да, такая конфигурация полностью предотвратит сжатие и запутывание библиотеки, если только вы не соберете ее локальную копию из исходного кода и не удалите оттуда такие полезные consumer-proguard-rules.pro.

Оценка зависимостей для Proguard

Как показано выше, даже опытные разработчики иногда игнорируют Proguard. Если поиск в Google относительно библиотеки и ее совместимости с Proguard ничего не возвращает (и даже если они делают какие-то результаты!), вам, возможно, придется принять собственное решение относительно использования Proguard. Вот как я лично делаю:

  • Если где-то на сайте библиотеки есть слова "framework", "enterprise", "reflection", скорее всего, она плохо совместима с Proguard;
  • Если библиотека имеет какое-либо отношение к генерации кода во время компиляции (например, Butterknife, Dagger и т. д.), подумайте дважды, прежде чем использовать Proguard;
  • Если библиотека путается с JNI, подумайте еще пару раз, прежде чем использовать для нее Proguard, и погуглите, чтобы узнать о ее влиянии на Proguard, даже если вы не сжимаете саму библиотеку;
  • Если вы сомневаетесь, погуглите и/или прочитайте исходный код библиотеки: использование Class.forName, а также Proxy.getInvocationHandler и подобного кода отражения обычно является плохим признаком.

Библиотеки, которые предлагают компоненты пользовательского интерфейса Android (например, MPAndroidChart), обычно можно уменьшить, по крайней мере, если вы сохраните getDefaultProguardFile('proguard-android.txt') в своей конфигурации Gradle.

Самая важная часть

Многие разработчики (включая самих разработчиков Proguard!) предложат вам ошибочную рекомендацию начать с пустой конфигурации Proguard + конфигурации Android Proguard по умолчанию и в конечном итоге добавить -keep правил, когда это необходимо.

НЕ ДЕЛАЙТЕ ЭТОГО!!

Эти советы исходят от людей, которые либо слишком круты, чтобы понять проблемы среднего разработчика (читай: «сам разработчик Proguard»), либо не имеют ни малейшего представления о том, как правильно использовать Proguard. На самом деле, подобные ошибочные действия являются той самой причиной, по которой многие ответы на этот вопрос предостерегают вас от использования Proguard: его поведение по умолчанию похоже на предложение кому-то начать альпинизм с восхождения на Эверест.

Конфигурация Proguard по умолчанию запутывает, сжимает и оптимизирует все — все ваше приложение со всеми зависимостями, кроме некоторых классов, которые вы явно исключили. Вы не хотите этого, если у вас нет абсолютного понимания каждой библиотеки и строки кода в ваших проектах: как они работают и взаимодействуют друг с другом, какие методы они используют внутри и т. д.

Вместо этого вы хотите сделать минимально необходимое вмешательство (уменьшить код, чтобы уменьшить количество методов dex) в минимально возможном диапазоне (несколько самых больших библиотек) с минимальными последствиями (только там, где точно известно, что Proguard работает). Вот мой конфиг Proguard для таких случаев:

-dontoptimize
-dontobfuscate

# Prints some helpful hints, always add this option
-verbose

-keepattributes SourceFile,LineNumberTable,Exceptions,InnerClasses,Signature,Deprecated,*Annotation*,EnclosingMethod


# add all known-to-be-safely-shrinkable classes to the beginning of line below
-keep class !com.android.support.**,!com.google.android.**,** { *; }

Добавьте приведенные выше правила в proguard-rules.pro вашего приложения, они будут сокращать только те классы, которые вы явно разрешаете сокращать. Добавьте подстановочные знаки для других безопасно сжимаемых пакетов (точно так же, как указано выше — с частями ! и .**) в начало строки -keep.

person user1643723    schedule 20.07.2015
comment
Это выглядит как хорошее обширное руководство. Буду следить за ним при работе с proguard! Большое спасибо! - person pebbles; 20.07.2015
comment
У меня немного другая конфигурация, которую я хотел бы сравнить с вашей. Я опубликую его как отдельный ответ, так как очень некрасиво помещать код в комментарии к SO. - person Alix; 20.10.2017
comment
На самом деле это совершенно разные вещи, но у нас одна и та же цель: избежать ада обслуживания и не использовать обфускацию или что-то еще, что может сделать приложение хрупким... - person Alix; 20.10.2017

В качестве альтернативы ProGuard вы можете использовать встроенный Gradle-уменьшитель. отключив ProGuard, но по-прежнему ссылаясь на конфигурацию ProGuard. Это удалит неиспользуемый код, но не запутает или не сделает какую-либо другую «магию». Хотя рекомендуется только для сборок Debug, я не понимаю, почему вы не можете использовать его и для сборок Release, если вы не считаете, что вам нужна обфускация.

Основное преимущество по сравнению с ProGuard (на мой взгляд) заключается в том, что вы избегаете жесткой связи между конфигурацией ProGuard и структурой вашей кодовой базы и сторонними зависимостями.

построить.градле:

minifyEnabled true
useProguard false
proguardFiles ('proguard-basic.pro', getDefaultProguardFile('proguard-android.txt'))

proguard-basic.pro:

-dontwarn javax.**
-keep class com.mycompany.** { *; }
person Alix    schedule 20.10.2017
comment
Эм... эта конфигурация вообще не использует Proguard. useProguard false полностью отключает Proguard, поэтому значение proguardFiles и соответствующее им содержимое не имеют значения. Я не понимаю, как эта конфигурация делает приложение менее хрупким (если только под менее хрупким вы не имеете в виду не использовать Proguard для начала). Кроме того, почему вы используете -dontwarn javax.**? Это выглядит действительно произвольно. По крайней мере, сделайте это -dontwarn javax.annotations.** или что-то в этом роде. - person user1643723; 20.10.2017
comment
Это может выглядеть так, но попробуйте. Файл proguard используется... Стимулятором. - person Alix; 20.10.2017
comment
Насчет предупреждений javax: на их пропажу жаловался шрустер. Поскольку они являются частью платформы на устройстве, я думаю, что можно их подавить. - person Alix; 21.10.2017
comment
Интересно. Я совсем пропустил введение дополнительного сжимателя для быстрого бега. Это правда, что Proguard несколько медлителен и имеет тенденцию делать неожиданные вещи (например, удаление данных отладки), поэтому новый сжиматель может быть полезен. Я предлагаю вам уточнить, что он отличается от Proguard (и, вероятно, связан с конкретный раздел статьи). - person user1643723; 21.10.2017

Если вы включите минификацию через ProGuard, это также минимизирует ваши зависимости.

Библиотеки, как правило, еще не обфусцированы/минимизированы с помощью ProGuard. Некоторые библиотеки не будут работать должным образом по умолчанию, если они запутаны, поэтому вам следует проверить все библиотеки, которые вы используете, чтобы узнать, есть ли у них какая-либо документация, связанная с ProGuard. Butterknife, например, имеет несколько специальных правил ProGuard, которые необходимо включить, чтобы убедиться, что он продолжает работать должным образом.

person Bryan Herbst    schedule 20.07.2015

Для меня вам следует искать multidex, чтобы выйти за пределы 65k, а не proguard, так как в более позднем периоде это не решение ваших проблем.

См. документы: https://developer.android.com/tools/building/multidex.html< /а>

person Marcin Orlowski    schedule 20.07.2015
comment
хм почему ты думаешь? я думаю, что более элегантно / профессионально (мы) не иметь этих надутых apk, не так ли? - person pebbles; 20.07.2015
comment
Да, всегда лучше иметь APK меньшего размера (и вы, вероятно, все равно собираетесь запускать proguard + minify, прежде чем переходить к производству), НО вы все равно можете выйти за пределы 65k методов даже после минимизации. Включение multidex — это «правильный» способ решить проблему ограничения метода в 65 тыс. - person ebernie; 20.07.2015
comment
Итак, вы, ребята, имеете в виду, что включение мультидекса необходимо в любом случае, чтобы быть в безопасности? Поскольку он прост в использовании, я просто реализовал его. Но я все еще хочу уменьшить свой код;) - person pebbles; 20.07.2015
comment
Да, включение multidex решает вашу проблему directly. Использование proguard может (по-прежнему, временно и без каких-либо гарантий) решить проблему, но это будет скорее побочный эффект. Но правильное решение подходит для multidex. - person Marcin Orlowski; 20.07.2015

Если вы включите минимизацию в файле build.grade, то да, это также сократит ваши зависимости.

Имейте в виду, что Proguard может вызвать нежелательные побочные эффекты. Не все библиотеки/зависимости можно уменьшить, поскольку Proguard также запутывает код. (т.е. превращает String name в String n) и удаляет неиспользуемый код.

Взгляните на этот проект Github: https://github.com/krschultz/android-proguard-snippets

В качестве альтернативы вы можете использовать MultiDex. Вы можете прочитать об этом здесь: https://developer.android.com/tools/building/multidex.html

person michaelcarrano    schedule 20.07.2015

В соответствии с новым обновлением Android Studio в версии 3.2 новый сжиматель кода, который также запутывает, добавляя строку ниже в файл gradle.properties вашего проекта.

Добавьте эту строку:

android.enableR8 = true
person amit pandya    schedule 06.10.2018