java ProGuard удалить (сжать) неиспользуемые классы

Допустим, у меня есть это Java-приложение:

package com.site;

public class MyAppBase {}

package com.site.free;

import com.site.MyAppBase;

public class MyApp extends MyAppBase {}

package com.site.pro;

import com.site.MyAppBase;

public class MyApp extends MyAppBase {}

package com.site;

public class Edition
{

    public static final int     FREE    = 1;
    public static final int     PRO     = 2;

    private static final int    EDITION = PRO;

    public static boolean is(final int edition)
    {
        return (EDITION == edition);
    }
}

package com.site;

public class EditionFactory
{

    public static MyAppBase get_app()
    {
        MyAppBase ret = null;

        if (Edition.is(Edition.FREE))
            ret = new com.site.free.MyApp();
        else if (Edition.is(Edition.PRO))
            ret = new com.site.pro.MyApp();

        return ret;
    }
}

Теперь любая комбинация конфигурации ProGuard, которую я пытаюсь избавиться от невыбранной версии (в данном случае это БЕСПЛАТНО), не работает.

Под «избавлением» я подразумеваю исчезновение фактического класса (а также из вызывающего кода).

Другими словами, такой вызов:

final MyAppBase app = EditionFactory.get_app();

.. в настоящее время переводится после ProGuarding на это:

if (a.a(1))
    localObject5 = new c(); // <<< FREE
else if (a.a(2))
    localObject5 = new d(); // <<< PRO

.. в то время как я бы хотел, чтобы это было переведено на это:

localObject5 = new d(); // <<< PRO only in the code (as set at Edition.EDITION)

Итог (помимо того факта, что ProGuard ВЕЛИКОЛЕПЕН!!), я не могу заставить его "видеть насквозь" и понимать что Edition.is() — это логическая функция, возвращающая константу, которая позволяет удалить некоторые классы.

Я пробовал такие конфигурации, как:

-keep,allowshrinking,allowoptimization public class * extends com.site.MyAppBase
-optimizationpasses 10

.. ничего не работает.

С другой стороны, если я ссылаюсь на Edition.EDITION и сравниваю его со встроенным (то есть без каких-либо «прокси-функций»), компилятор Java (v1.6) обнаруживает его и удаляет всю ссылку на невыбранный выпуск/класс.
Это приводит к тому, что ProGuard удаляет/уменьшает неиспользуемый класс, и это здорово.

Единственная проблема здесь связана с поддержанием — я был бы рад сохранить возможность использовать стиль EditionFactory.


person Poni    schedule 15.08.2011    source источник


Ответы (2)


Оптимизация не выполняется, поскольку ProGuard решает не встраивать метод Edition#is, поэтому он не может упростить результирующий ряд инструкций. Метод не встроен, потому что он не очень короткий и к тому же вызывается более одного раза. Вы можете обойти первый критерий с помощью этой недокументированной опции JVM для ProGuard:

-Dmaximum.inlined.code.length=16

В качестве альтернативы вы можете обойти второй критерий, убедившись, что метод вызывается только один раз:

return Edition.is(Edition.FREE) ?
    new com.site.free.MyApp() :
    new com.site.pro.MyApp();

В качестве альтернативы вы могли бы, вероятно, создать короткие функции isFree() и isPro(), потому что они будут возвращать константы, которые будут встроены.

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

Приятно слышать, что вам нравится ProGuard.

person Eric Lafortune    schedule 15.08.2011

Это потому, что ваш метод is(...) может принимать больше значений, чем только 1 и 2. Его можно было бы вызвать из кода где угодно с 3, 4, 6... Proguard не может исключить этот случай.

Решение вашей проблемы состоит в том, чтобы сделать Edition enum. Вам больше не понадобится метод is(...), и вы сможете положиться на equals(...).

person Jérôme Verstrynge    schedule 15.08.2011
comment
Вы имеете в виду сделать Edition enum? - person Kevin Reid; 15.08.2011
comment
Ой, извините, опечатка, я имел в виду издание. - person Jérôme Verstrynge; 15.08.2011
comment
@Poni Это должно работать, если EDITION является перечислимым значением Edition и если оно является статическим окончательным. Если у вас есть только два значения, вы можете попробовать и логическое значение. - person Jérôme Verstrynge; 15.08.2011
comment
@JVerstry Я понимаю, о чем вы говорите, это не работает, попробуйте сами .. Будет интересно посмотреть, найдет ли кто-нибудь ответ на этот вопрос. - person Poni; 15.08.2011