I am developing an Android app in Android Studio that uses gRPC. One fine day, I started seeing an odd runtime error that crashed the app:
08-02 16:45:42.695 2756-2756/com.abdulwasae.protobug E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.abdulwasae.protobug, PID: 2756
java.lang.VerifyError: Verifier rejected class protobug.v0.GetTimeResp: void protobug.v0.GetTimeResp.mergeTheTime(com.google.protobuf.Timestamp) failed to verify: void protobug.v0.GetTimeResp.mergeTheTime(com.google.protobuf.Timestamp): [0x26] register v5 has type Precise Reference: com.google.protobuf.Timestamp but expected Reference: com.google.protobuf.GeneratedMessageLiteVerifier rejected class protobug.v0.GetTimeResp: java.lang.Object protobug.v0.GetTimeResp.dynamicMethod(com.google.protobuf.GeneratedMessageLite$MethodToInvoke, java.lang.Object, java.lang.Object) failed to verify: java.lang.Object protobug.v0.GetTimeResp.dynamicMethod(com.google.protobuf.GeneratedMessageLite$MethodToInvoke, java.lang.Object, java.lang.Object): [0x8C] register v8 has type Precise Reference: com.google.protobuf.Timestamp but expected Reference: com.google.protobuf.GeneratedMessageLite (declaration of 'protobug.v0.GetTimeResp' appears in /data/app/com.abdulwasae.protobug-1/split_lib_slice_6_apk.apk)
at protobug.v0.GetTimeResp.getDefaultInstance(GetTimeResp.java:0)
at protobug.v0.TheServiceGrpc.<clinit>(TheServiceGrpc.java:38)
at protobug.v0.TheServiceGrpc.newBlockingStub(TheServiceGrpc.java:0)
at com.abdulwasae.protobug.Client.getBlockingStub(Client.java:72)
at com.abdulwasae.protobug.Client.getTime(Client.java:77)
at com.abdulwasae.protobug.MainActivity.onClick(MainActivity.java:25)
at android.view.View.performClick(View.java:5637)
at android.view.View$PerformClick.run(View.java:22429)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6119)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
Somehow, even when I reverted to an older version of the app that was definitely working before, I still saw this problem.
I narrowed it down to this: if you remove all fields that use google.protobuf.Timestamp from the protocol buffer (i.e. protobuf) file, then we no longer see the error. This is true even if I import "google/protobuf/timestamp.proto" in the protobuf file, but don't declare any field of type Timestamp.
Here is the MWE of the problem that you can directly open up in Android Studio: https://github.com/abdulwasae/google.protobuf.timestamp-java.lang.VerifyError
To replicate the problem, simply run the app, and press the "Touch" button at the center of the screen.
I have tested this on an emulator running Pixel XL w API 25, on a Nexus 6p w API 25, and on a Samsung Galaxy J5 w API 23. I have also compiled and built the same code from three separate machines (two macOS, one Windows 10).
You are depending on the wrong protobuf runtime:
https://github.com/abdulwasae/google.protobuf.timestamp-java.lang.VerifyError/blob/master/app/build.gradle#L67
'com.google.protobuf:protobuf-java:3.1.0' is the full runtime and won't work with protoc-gen-javalite. Please use 'com.google.protobuf:protobuf-lite:3.0.1' instead.
@xfxyjwf We need to use message type google.protobuf.Timestamp.proto, and com.google.protobuf:protobuf-lite:3.0.1 does not have it. What is the proper way to deal with that then?
Problem solved -- our understanding of dependencies was limited.
The problem is in the following two lines:
compile 'com.google.api.grpc:googleapis-common-protos:0.0.3'
protobuf 'com.google.protobuf:protobuf-java:3.1.0'
The common-protos compile dependency is dependent on protobuf-java. This gives us duplicate Timestamp classes: one defined in our build/generated folder, and the other defined in the protobuf-java external library (which was included because it is a dependency of common-protos).
Previously we thought that using protobuf-lite as a compile dependency was wrong, because it did not contain a Timestamp type. We now understand that protobuf-lite does not contain every possible type -- instead, we get the types we need via code generation, which requires protobuf-java as a dependency.
So, we only need protobuf-lite as a compile dependency, and protobuf-java as a code-generation dependency. The protobuf compiler will only generate the types/classes we actually make use of, which is why I suppose the compile dependency is called "lite".
Thanks to @xfxyjwf for shedding light on this.
@ismail-khan pllease can I see your build.gradle? I have same problem.
@ismail-khan Could you share your gradle config? same problem here. :)
Im having the same problem here, how to deal with timestamp.proto if only using protobuf-lite?
app/build.gradle:
dependencies {
implementation 'com.google.protobuf:protobuf-lite:3.0.1'
}
/build.gradle:
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.google.protobuf:protobuf-java:3.4.0'
}
It worked for me.
Most helpful comment
Problem solved -- our understanding of dependencies was limited.
The problem is in the following two lines:
The common-protos compile dependency is dependent on protobuf-java. This gives us duplicate Timestamp classes: one defined in our build/generated folder, and the other defined in the protobuf-java external library (which was included because it is a dependency of common-protos).
Previously we thought that using protobuf-lite as a compile dependency was wrong, because it did not contain a Timestamp type. We now understand that protobuf-lite does not contain every possible type -- instead, we get the types we need via code generation, which requires protobuf-java as a dependency.
So, we only need protobuf-lite as a compile dependency, and protobuf-java as a code-generation dependency. The protobuf compiler will only generate the types/classes we actually make use of, which is why I suppose the compile dependency is called "lite".
Thanks to @xfxyjwf for shedding light on this.