使用 Proguard 后通过反射访问方法时出现 NoSuchMethodException
NoSuchMethodException when accessing method via reflection after using Proguard
我希望使用 Proguard 减少 Android 应用程序的大小和方法数。目前没有必要混淆代码。应用程序的 REST api 使用反射为端点调用适当的方法。这将在未来更改为回调,但目前我需要使用此方法。
此方法在请求成功时调用,并调用相应的方法,下面的错误信息中saltSucceeded。
public void onSuccess(int statusCode, String response) {
try {
getClass().getDeclaredMethod(action + "Succeeded", Integer.TYPE, String.class).invoke(this, statusCode, response);
} catch (Exception e) {
Logger.error(e);
callFailedMethod();
}
}
当 Proguard 运行 从 salt 操作调用 onSuccess 时,会出现此错误。
ERROR: ResponseHandler.onSuccess(113) ->
java.lang.NoSuchMethodException: saltSucceeded [int, class java.lang.String]
at java.lang.Class.getConstructorOrMethod(Class.java:472)
at java.lang.Class.getDeclaredMethod(Class.java:640)
at com.someapp.api.response.ResponseHandler.onSuccess(ResponseHandler.java:110)
at com.someapp.api.response.ResponseHandler.onSuccess(ResponseHandler.java:94)
at com.loopj.android.http.AsyncHttpResponseHandler.handleMessage(AsyncHttpResponseHandler.java:359)
at com.loopj.android.http.AsyncHttpResponseHandler$ResponderHandler.handleMessage(AsyncHttpResponseHandler.java:183)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5356)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(Native Method)
这里是 proguard-rules.pro 文件。
-dontobfuscate
-dontoptimize
-dontpreverify
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
-printseeds seeds.txt
-printusage unused.txt
-printmapping mapping.txt
-printconfiguration configuration.txt
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable
-keepclasseswithmembers class com.someapp.** { *; }
# Library Specific Configurations follows
...
从app/build.gradle:
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
不足为奇 unused.txt
...
27:28:public void saltFailed(java.lang.Throwable,java.lang.String)
32:41:public void saltSucceeded(int,java.lang.String)
...
我查看了几个网站和参考资料,讨论了 Proguard 和反射的问题,但是各种建议都没有解决问题。目前我正试图阻止混淆器修改 com.someapp 代码以尝试恢复到工作状态。从那里我可以花更多时间尝试只保留方法:*失败、*成功、*完成、*NoConnection。
感谢您的任何意见。
http://www.alexeyshmalko.com/2014/proguard-real-world-example/
Proguard and reflection in Android
选项 -keepclasseswithmembers 有特殊含义,更多用于查找主要 classes 及其 main 方法,例如。如有疑问,只需使用 -keep。以下应该有效:
-keepclassmembers class com.someapp.api.response.ResponseHandler {
public void *Succeeded(...);
public void *Complete(...);
public void *NoConnection(...);
public void *Failed(...);
}
注意完全限定的 class 名称(如果它看起来不正确或不完整,ProGuard 会打印出一条注释)。我在参数中使用了通配符,但如果您愿意,可以更具体一些。
如果你想匹配扩展中的方法,你需要一个单独的选项:
-keepclassmembers class * extends com.someapp.api.response.ResponseHandler {
public void *Succeeded(...);
public void *Complete(...);
public void *NoConnection(...);
public void *Failed(...);
}
您应该会在 seeds.txt 中看到匹配的方法。标准 Android 构建过程已经将此文件写到不同的位置(例如 build/outputs/mapping/release/seeds.txt in Gradle)。
我希望使用 Proguard 减少 Android 应用程序的大小和方法数。目前没有必要混淆代码。应用程序的 REST api 使用反射为端点调用适当的方法。这将在未来更改为回调,但目前我需要使用此方法。
此方法在请求成功时调用,并调用相应的方法,下面的错误信息中saltSucceeded。
public void onSuccess(int statusCode, String response) {
try {
getClass().getDeclaredMethod(action + "Succeeded", Integer.TYPE, String.class).invoke(this, statusCode, response);
} catch (Exception e) {
Logger.error(e);
callFailedMethod();
}
}
当 Proguard 运行 从 salt 操作调用 onSuccess 时,会出现此错误。
ERROR: ResponseHandler.onSuccess(113) ->
java.lang.NoSuchMethodException: saltSucceeded [int, class java.lang.String]
at java.lang.Class.getConstructorOrMethod(Class.java:472)
at java.lang.Class.getDeclaredMethod(Class.java:640)
at com.someapp.api.response.ResponseHandler.onSuccess(ResponseHandler.java:110)
at com.someapp.api.response.ResponseHandler.onSuccess(ResponseHandler.java:94)
at com.loopj.android.http.AsyncHttpResponseHandler.handleMessage(AsyncHttpResponseHandler.java:359)
at com.loopj.android.http.AsyncHttpResponseHandler$ResponderHandler.handleMessage(AsyncHttpResponseHandler.java:183)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:157)
at android.app.ActivityThread.main(ActivityThread.java:5356)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
at dalvik.system.NativeStart.main(Native Method)
这里是 proguard-rules.pro 文件。
-dontobfuscate
-dontoptimize
-dontpreverify
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-verbose
-printseeds seeds.txt
-printusage unused.txt
-printmapping mapping.txt
-printconfiguration configuration.txt
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable
-keepclasseswithmembers class com.someapp.** { *; }
# Library Specific Configurations follows
...
从app/build.gradle:
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
不足为奇 unused.txt
...
27:28:public void saltFailed(java.lang.Throwable,java.lang.String)
32:41:public void saltSucceeded(int,java.lang.String)
...
我查看了几个网站和参考资料,讨论了 Proguard 和反射的问题,但是各种建议都没有解决问题。目前我正试图阻止混淆器修改 com.someapp 代码以尝试恢复到工作状态。从那里我可以花更多时间尝试只保留方法:*失败、*成功、*完成、*NoConnection。
感谢您的任何意见。
http://www.alexeyshmalko.com/2014/proguard-real-world-example/
Proguard and reflection in Android
选项 -keepclasseswithmembers 有特殊含义,更多用于查找主要 classes 及其 main 方法,例如。如有疑问,只需使用 -keep。以下应该有效:
-keepclassmembers class com.someapp.api.response.ResponseHandler {
public void *Succeeded(...);
public void *Complete(...);
public void *NoConnection(...);
public void *Failed(...);
}
注意完全限定的 class 名称(如果它看起来不正确或不完整,ProGuard 会打印出一条注释)。我在参数中使用了通配符,但如果您愿意,可以更具体一些。
如果你想匹配扩展中的方法,你需要一个单独的选项:
-keepclassmembers class * extends com.someapp.api.response.ResponseHandler {
public void *Succeeded(...);
public void *Complete(...);
public void *NoConnection(...);
public void *Failed(...);
}
您应该会在 seeds.txt 中看到匹配的方法。标准 Android 构建过程已经将此文件写到不同的位置(例如 build/outputs/mapping/release/seeds.txt in Gradle)。