Проблема Proguard с SAAgent (Samsung Accessory) java.lang.NoSuchMethodException: <init> []
Я пытаюсь использовать Proguard с моим Android-приложением и использую SDK Samsung, который продолжает давать неприятности.
Независимо от того, что я пробую в конфигурации Proguard, я не могу обойти это исключение времени выполнения:
07-21 13:44:12.851: E/SAAgent(3563): <init> []
07-21 13:44:12.851: E/SAAgent(3563): java.lang.NoSuchMethodException: <init> []
...
07-21 13:44:12.851: E/AndroidRuntime(3563): Caused by: java.lang.RuntimeException: Invalid implemetation of SASocket. Provider a public default constructor.
...
У кого-нибудь есть идеи, что попробовать?
3 ответа
Проблема в том, что при некоторой оптимизации Proguard изменит каждый внутренний класс в классе верхнего уровня.
Это означает, что конструктор по умолчанию для внутреннего класса будет заменен конструктором с одним параметром, который принимает экземпляр внешнего класса, потому что в java внутренний класс сохраняет ссылку на внешний класс.
Для Samsung Accesory SDK требуется конструктор по умолчанию для реализации внутреннего класса SASocket, потому что, я думаю, они используют отражение для создания экземпляра этого объекта.
Здесь http://sourceforge.net/p/proguard/bugs/387/ вы можете прочитать, что: "Outer$Inner не заменен классом верхнего уровня, если вы также не добавите в конфигурацию -repackageclasses и -allowaccessmodification".
К сожалению, эти флаги обычно наследуются от proguard-android-optimize.txt, и если вы хотите сохранить оптимизацию, решение добавляется в конфигурацию вашей proguard:
-keepattributes InnerClasses
Обратите внимание, что для того, чтобы использовать все функции Samsung Accesory SDK, вы должны также включить следующие правила:
# Based on http://proguard.sourceforge.net/manual/examples.html#library
-keep public class com.samsung.** {
public protected *;
}
-keepclassmembernames class com.samsung.** {
java.lang.Class class$(java.lang.String);
java.lang.Class class$(java.lang.String, boolean);
}
-keepclasseswithmembernames class com.samsung.** {
native <methods>;
}
-keepclassmembers enum com.samsung.** {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keepclassmembers class com.samsung.** implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
Просто добавьте этот ответ в качестве альтернативы и обновите уже существующий ответ.
Недавно интегрировав Samsung Accessory SDK в приложение-компаньон для Android для поддержки приложения Tizen на Galaxy Gear S2, я столкнулся с той же проблемой при компиляции (минимизированной) версии выпуска сопутствующего приложения.
Ответ @while уже объясняет причину и способ устранения NoSuchMethodException
выброшены. Тем не менее, я обнаружил, что изложенные правила Proguard довольно смягчены и многочисленны. Это может быть необходимо для более старых версий Accessory SDK, но в наши дни вы, вероятно, можете сделать это с гораздо меньшими исключениями.
Предполагая, что вы используете одну из последних версий Accessory SDK (я тестировал и 2.2.2, и 2.3.0), вы все равно захотите начать с:
-keepattributes InnerClasses
Вы не можете обойти это, потому что SAAgent
использует отражение, чтобы создать экземпляр экземпляра SASocket
Вы реализуете где-то в своем собственном коде. Это правило гарантирует, что отношения между (и именованием) внутреннего и внешнего классов не изменятся.
Теперь у вас может возникнуть желание написать правило, чтобы оставить конструктор по умолчанию для вашего SASocket
реализация путем добавления исключения для <init>()
, К сожалению, это не сработает, потому что в рамках оптимизации кода Proguard фактически создаст параметризованный конструктор во внутреннем классе, который принимает экземпляр внешнего класса. В результате этот конструктор не хранится и не удаляется, потому что Proguard думает, что его никто не вызывает.
Итак, короче говоря, чтобы сохранить ваши SASocket
Реализация и ее конструктор (ы), добавить правило:
-keep class * extends com.samsung.android.sdk.accessory.SASocket { <init>(...); }
До этого момента ваше приложение, вероятно, зависало во время выполнения без вышеуказанных правил. Добавив их, это больше не должно иметь место. Однако вы заметите, что Accessory SDK по-прежнему регистрирует различные ошибки и что ваше приложение еще не работает должным образом. Осматривая Logcat, вы должны увидеть ошибки, указывающие на то, что SDK не удалось привязать к вашей службе.
Причина этого может быть не очевидна, но если вы покопаетесь в Accessory SDK, вы заметите некоторые IInterface
а также Binder
расширения (т.е. в IDeathCallback
а также ISAFrameworkManager
(2.2.2) или ISAFrameworkManagerV2
(2.3.0)). Поскольку Proguard не может найти какие-либо явные вызовы к ним и не знает, что они на самом деле вызываются во время выполнения платформой Android, он отбросит их. Итак, давайте добавим правило, чтобы Proguard не делал этого:
-keep class com.samsung.accessory.api.* extends android.os.Binder { *; }
После этого пришло время для поздравлений: ваш сервис должен снова быть привязанным. В зависимости от вашей реализации, вам могут потребоваться дополнительные исключения, но для базовой настройки вышеприведенное должно помочь.
Добавляя все это, вы должны иметь следующие правила в вашей конфигурации:
#
# Samsung Accessory SDK Proguard Rules
#
# Keep relationship between inner and outer classes
-keepattributes InnerClasses
# Keep any SASocket implementation and its constructors
-keep class * extends com.samsung.android.sdk.accessory.SASocket { <init>(...); }
# Keep the Accessory SDK's IInterface and Binder classes
-keep class com.samsung.accessory.api.* extends android.os.Binder { *; }
YMMV.
Попробовал предложения выше, но все еще получал сбои, связанные с интерфейсами Parcelable, реализованными SAPeerAccessory (и, возможно, некоторыми другими) и другими классами, реализующими IInterface:
public class SAPeerAccessory implements Parcelable
У меня также были проблемы с моими собственными классами, сериализуемыми через GSON (последняя строка в примере ниже). Вот так выглядят мои изменения в proguard-rules.pro
-keepattributes SourceFile,LineNumberTable,InnerClasses,EnclosingMethod,Signature
-keep class * extends com.samsung.android.sdk.accessory.SASocket { <init>(...); }
# Keep the Accessory SDK's IInterface, Binder, and Prcelable classes
-keep class com.samsung.** extends android.os.Binder { *; }
-keep class com.samsung.** extends android.os.IInterface { *; }
-keep class com.samsung.** extends android.os.Parcelable { *; }
# This is for my own class implementing a model serializable by GSON
-keep class my.gson.Model { <fields>; }
Пока не происходит сбоев, размер APK уменьшен на 20%
Lib версии Samsung в моем проекте:
accessory-v2.5.3.jar
sdk-v1.0.0.0.jar