SAAgent(三星附件)的Proguard问题java.lang.NoSuchMethodException:<init> [] </init>

时间:2014-07-21 18:51:54

标签: java android proguard samsung-mobile samsung-mobile-sdk

我正在尝试使用我的Android应用程序使用proguard并使用三星附件sdk一直给它带来麻烦。

无论我在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 个答案:

答案 0 :(得分:4)

问题是,在启用一些优化后,Proguard将改变顶级类中的每个内部类。

这意味着内部类的默认构造函数将与一个参数构造函数交换,该构造函数接受外部类的实例,因为在java中,内部类保留对外部类的引用。

Samsung Accesory SDK需要SASocket内部类实现的默认构造函数,因为我猜他们使用反射来实例化该对象。

在这里http://sourceforge.net/p/proguard/bugs/387/,您可以读到:“外部$ 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(); 
}

答案 1 :(得分:2)

只需将此答案添加为现有答案的替代更新。

最近将三星的Accessory SDK集成到Android'配套'应用程序中以支持Galaxy Gear S2上的Tizen应用程序,在编译随附应用程序的(缩小版)版本时遇到了同样的问题。

@ 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无法绑定到您的服务。

原因可能并不明显,但如果您在附件SDK中挖掘,您会注意到一些IInterfaceBinder扩展名(即IDeathCallback和{{1} (2.2.2)或ISAFrameworkManager(2.3.0))。由于Proguard无法找到对它们的任何显式调用,并且不知道这些实际上是由Android框架在运行时调用的,因此它会将它们剥离。所以,让我们添加一条规则来阻止Proguard这样做:

ISAFrameworkManagerV2

在此之后,是时候祝贺了:您的服务现在应该再次绑定。根据您的实现,您可能需要进一步的例外,但对于基本设置,上面应该可以解决问题。

全部添加,您的配置中应该有以下规则:

-keep class com.samsung.accessory.api.* extends android.os.Binder { *; }

因人而异。

答案 2 :(得分:0)

尝试了以上建议,但仍将崩溃归因于由 SAPeerAccessory (可能还有其他一些)和实现 IInterface <的其他类实现的 Parcelable 接口/ strong>:

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版本

accessory-v2.5.3.jar
sdk-v1.0.0.0.jar