4

I've tried countless things but I'm not being able to prevent my app to crash while using Proguard to shrink the code (obfuscation is disabled). I always get the following exception when Proguard is enabled:

04-03 10:26:37.277 E/AndroidRuntime(29460): FATAL EXCEPTION: main
04-03 10:26:37.277 E/AndroidRuntime(29460): java.lang.ExceptionInInitializerError
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.company.nativeapp.activitycontrollers.LoginController.onLogInSuccess(LoginController.java:199)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.company.datamanager.BfAccountStateManager$AccountWebViewClient.shouldOverrideUrlLoading(BfAccountStateManager.java:326)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at android.webkit.CallbackProxy.uiOverrideUrlLoading(CallbackProxy.java)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at android.webkit.CallbackProxy.handleMessage(CallbackProxy.java)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at android.os.Handler.dispatchMessage(Handler.java)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at android.os.Looper.loop(Looper.java)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at android.app.ActivityThread.main(ActivityThread.java)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at java.lang.reflect.Method.invokeNative(Native Method)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at java.lang.reflect.Method.invoke(Method.java:511)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1028)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:795)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at dalvik.system.NativeStart.main(Native Method)
04-03 10:26:37.277 E/AndroidRuntime(29460): Caused by: java.lang.ExceptionInInitializerError
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.company.android.bfsdk.diffusion.requests.mcs.BaseMCSRequest.<clinit>(BaseMCSRequest.java:28)
04-03 10:26:37.277 E/AndroidRuntime(29460):     ... 12 more
04-03 10:26:37.277 E/AndroidRuntime(29460): Caused by: java.lang.ExceptionInInitializerError
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.company.mobile.mcs.service.descriptor.inplay.Inplay.<clinit>(Inplay.java:20585)
04-03 10:26:37.277 E/AndroidRuntime(29460):     ... 13 more
04-03 10:26:37.277 E/AndroidRuntime(29460): Caused by: java.lang.RuntimeException: Generated message class "com.company.mobile.mcs.service.descriptor.Mcs$MCSRequestMessage$Builder" missing method "getUserAgent".
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.google.protobuf.GeneratedMessage.getMethodOrDie(GeneratedMessage.java:1359)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.google.protobuf.GeneratedMessage.access$1300(GeneratedMessage.java:57)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.google.protobuf.GeneratedMessage$FieldAccessorTable$SingularFieldAccessor.<init>(GeneratedMessage.java:1485)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.google.protobuf.GeneratedMessage$FieldAccessorTable.<init>(GeneratedMessage.java:1432)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.company.mobile.mcs.service.descriptor.Mcs$1.assignDescriptors(Mcs.java:2083)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.google.protobuf.Descriptors$FileDescriptor.internalBuildGeneratedFileFrom(Descriptors.java:298)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.company.mobile.mcs.service.descriptor.Mcs.<clinit>(Mcs.java:2109)
04-03 10:26:37.277 E/AndroidRuntime(29460):     ... 14 more
04-03 10:26:37.277 E/AndroidRuntime(29460): Caused by: java.lang.NoSuchMethodException: getUserAgent []
04-03 10:26:37.277 E/AndroidRuntime(29460):     at java.lang.Class.getConstructorOrMethod(Class.java:460)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at java.lang.Class.getMethod(Class.java:915)
04-03 10:26:37.277 E/AndroidRuntime(29460):     at com.google.protobuf.GeneratedMessage.getMethodOrDie(GeneratedMessage.java:1357)
04-03 10:26:37.277 E/AndroidRuntime(29460):     ... 20 more

Besides the standard Proguard configuration for Android apps, I've added the following lines:

-keep public class com.company.**

-keep class com.company.* { *; }
-keepclassmembernames class com.company.* { *; }

-keep class * extends com.google.protobuf.GeneratedMessage { *; }
-keepclassmembernames class * extends com.google.protobuf.GeneratedMessage { *; }

But I still get the exception mentioned above...

What am I missing?

rfgamaral
  • 16,546
  • 57
  • 163
  • 275
  • 1
    I tend to think the main benefit of Proguard is its obfuscation rather than its shrinking ability. So I just have the line "-dontshrink" in my config, – NickT Apr 03 '13 at 10:18
  • @NickT Yeah, but I really need the shrinking feature... See: http://stackoverflow.com/questions/15436956/how-to-solve-the-issue-with-dalvik-compiler-limitation-on-64k-methods – rfgamaral Apr 03 '13 at 10:55
  • @RicardoAmaral: Did you manage to resolve this? If yes please can you share your findings? Am stuck at the same issue for long time. – Atul Aug 11 '16 at 07:01
  • @Atul Probably, but this was more than 3 years ago, I don't remember the actual solution, sorry. Didn't the accepted answer help you? – rfgamaral Aug 11 '16 at 09:05
  • @RicardoAmaral: Nope. Although the accepted answer has some clues to try but it couldn't help me. Trying few other things. Anyways thanks for your reply. I just overlooked somehow, its three long years after you asked :) – Atul Aug 11 '16 at 09:11

2 Answers2

4

The problem is ProtocolBuffers is using reflection to call methods but Proguard can not see the reflection calls. Proguard could either change the name of a Method / Object or remove the Method / Object, either way reflection will not find it.

Options:

  • You could try Finding every possible reflection call and adding the appropriate Proguard statements in. But you might need to change this for each new version of protocol buffers. Personally I would not go this way

  • You could try adding optimize_for SPEED option to the Proto Definition and regenerating the java code, this will result in much larger class that does not use reflection and could be used with Proguard.

  • Try one of the JavaMe protobuf solutions - they are much smaller. see previous question

Problem Code :

 private static Method getMethodOrDie(
      final Class clazz, final String name, final Class... params) {
    try {
      return clazz.getMethod(name, params);
    } catch (NoSuchMethodException e) {
      throw new RuntimeException(
        "Generated message class \"" + clazz.getName() +
        "\" missing method \"" + name + "\".", e);
    }
  }
Community
  • 1
  • 1
Bruce Martin
  • 10,358
  • 1
  • 27
  • 38
  • 1
    But that's exactly what I don't understand... Proguard is not seeing the reflection calls and as such detects some methods which "are not being used" and removes them. But they shouldn't be removed because I told Proguard to not remove anything under com.company.**, or should they? – rfgamaral Apr 04 '13 at 08:36
  • We were already using proto files optimized for speed, but for some reason reflection was still being used!? However, I downloaded the latest Proto Buffer library and compiler, generated all the .java files again optimized for speed and it seems to be working now. Maybe we were using old .proto files or maybe this new version of Protocol Buffers helped in a way. It's fixed now, that's what matters. – rfgamaral Apr 04 '13 at 13:41
  • @Ricardo To keep _everything_ under com.company, you would need "-keep class com.company.** { *; }". That would however largely defeat the point of optimization and obfuscation. – Eric Lafortune Apr 04 '13 at 23:27
  • @EricLafortune I know, but for now the point is only to shrink because of this: http://stackoverflow.com/questions/15436956/how-to-solve-the-issue-with-dalvik-compiler-limitation-on-64k-methods But maybe your comment is the right answer to the question. If you want to create an answer for that, I'll switch the accepted answer. Also, that general rule was more for testing purposes, than I was going to fine tune it. – rfgamaral Apr 05 '13 at 08:22
  • I'm curious why proto class generated using "Optimised for Speed" option won't create problem in obfuscation. I'm getting error. – Dharmendra Sep 04 '20 at 13:46
1

I also encountered this issue: java.lang.RuntimeException Generated message class "xxx" missing method "xxx" in signed apk, but in debug mode it's out of question. And I'm sure the Proguard configuration is OK.

By analyzing the stack trace log, I found the root cause of the problem: print protobuf file log too huge, like

List<UserPb.UserInfo> userInfos = obj.getUserInfoList();

Log.d(TAG, "userInfos: " + userInfos);

this UserPb.java file is so huge that contains over 50000 lines of code.

So DO NOT print protobuf related log in signed apk.

Smiles
  • 481
  • 1
  • 6
  • 15