Проблема 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
Другие вопросы по тегам