stephanenicolas / robospice

Repo of the Open Source Android library : RoboSpice. RoboSpice is a modular android library that makes writing asynchronous long running tasks easy. It is specialized in network requests, supports caching and offers REST requests out-of-the box using extension modules.
Apache License 2.0
2.95k stars 545 forks source link

VerifyError on Android 5.0: Rejecting class GsonObjectPersister that attempts to sub-class erroneous class SpringAndroidObjectPersister #387

Open ajans opened 9 years ago

ajans commented 9 years ago

This error pops into Logcat with Robospice 1.4.14 (commit 5f66030) on Android 5.0 (Nexus 10 and Nexus 7) using SpiceArrayAdapter from com.octo.android.robospice.spicelist.simple and the GsonSpringAndroidSpiceService.

The stacktrace and exact error message is as follows:

E/art(2849): Rejecting class com.octo.android.robospice.persistence.springandroid.json.gson.GsonObjectPersister that attempts to sub-class erroneous class com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister in /data/app/de.billiger.android-1/base.apk
D//DefaultRequestRunner.java:219(2849): 14:16:08.282 Thread-1181 An unexpected error occurred when processsing request CachedSpiceRequest [requestCacheKey=https://api.billiger.de/content/2.0/get_category_tree, cacheDuration=86400000, spiceRequest=de.billiger.android.categories.CategoryTreeRequest@31262d71]
D//DefaultRequestRunner.java:219(2849): java.lang.VerifyError: Rejecting class com.octo.android.robospice.persistence.springandroid.json.gson.GsonObjectPersister that attempts to sub-class erroneous class com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister (declaration of 'com.octo.android.robospice.persistence.springandroid.json.gson.GsonObjectPersister' appears in /data/app/de.billiger.android-1/base.apk)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.persistence.springandroid.json.gson.GsonObjectPersisterFactory.createInFileObjectPersister(GsonObjectPersisterFactory.java:42)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.persistence.file.InFileObjectPersisterFactory.createObjectPersister(InFileObjectPersisterFactory.java:115)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.persistence.file.InFileObjectPersisterFactory.createObjectPersister(InFileObjectPersisterFactory.java:21)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.persistence.CacheManager.getObjectPersister(CacheManager.java:183)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.persistence.CacheManager.loadDataFromCache(CacheManager.java:68)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.request.DefaultRequestRunner.loadDataFromCache(DefaultRequestRunner.java:262)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.request.DefaultRequestRunner.processRequest(DefaultRequestRunner.java:99)
D//DefaultRequestRunner.java:219(2849):     at com.octo.android.robospice.request.DefaultRequestRunner$1.run(DefaultRequestRunner.java:217)
D//DefaultRequestRunner.java:219(2849):     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
D//DefaultRequestRunner.java:219(2849):     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
D//DefaultRequestRunner.java:219(2849):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
D//DefaultRequestRunner.java:219(2849):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
D//DefaultRequestRunner.java:219(2849):     at java.lang.Thread.run(Thread.java:818)
D//DefaultRequestRunner.java:219(2849): Caused by: java.lang.VerifyError: Rejecting class com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister because it failed compile-time verification (declaration of 'com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister' appears in /data/app/de.billiger.android-1/base.apk)
D//DefaultRequestRunner.java:219(2849):     ... 13 more

Do you need any more information?

ajans commented 9 years ago

this VerifyError seems to be related to this change in the ART-runtime:

https://android.googlesource.com/platform/art/+/1c5eb70%5E!/

stephanenicolas commented 9 years ago

Hi Arne,

but what is wrong with the super class ? Do you have any way to know it ? Usually such problem are logged at the WARNING level in logcat, prior to the error.

Thx for this bug report, it is quite unexpected though..

Stéphane

2014-12-04 9:13 GMT-05:00 Arne Jans notifications@github.com:

this VerifyError seems to be related to this change in the ART-runtime:

https://android.googlesource.com/platform/art/+/1c5eb70%5E!/

— Reply to this email directly or view it on GitHub https://github.com/stephanenicolas/robospice/issues/387#issuecomment-65636941 .

ajans commented 9 years ago

It may or may not be related to this Demo-Project with workaround for a similar VerifyError, albeit not referring to sub-classing:

https://github.com/aectann/android-verify-error-example

I will try the given workaround with the class GsonObjectPersisterFactory line 42 to see if it helps.

Logcat shows only one warning twice, which might be related:

W/art(7171): Attempt to remove local handle scope entry from IRT, ignoring

I will let you know what comes out of this.

ajans commented 9 years ago

The presumed workaround didn't change anything, the class GsonObjectPersister still gets rejected. As our latest app-release works with our customized version of robospice-1.4.2-SNAPSHOT, I think I might have to git bisect the robospice-history to determine which commit introduced the incompatible change.

But right before that, I have to check our toolchain, maybe our recently introduced DexGuard-tool introduced some sort of incompatibility, as we use its shrinking-capabilities it for our debugging-builds, too.

ajans commented 9 years ago

Disabling DexGuard doesn't help anything. I can confirm that the problem doesn't exist with robospice-1.4.2-SNAPSHOT for debug- and release-builds (release is obfuscated with Proguard/DexGuard).

My previous logcat-excerpt did filter too much, it was filtering by the application-name. If I do not filter anything in logcat, I can see the following output from dex2oat:

I/dex2oat(29475): /system/bin/dex2oat --zip-fd=6 --zip-location=/data/app/de.billiger.android-1/base.apk --oat-fd=7 --oat-location=/data/dalvik-cache/arm/data@app@de.billiger.android-1@base.apk@classes.dex --instruction-set=arm --instruction-set-features=default --runtime-arg -Xms64m --runtime-arg -Xmx512m
I/dex2oat(29475): Verification error in java.lang.Object com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister.readCacheDataFromFile(java.io.File)
I/dex2oat(29475): java.lang.Object com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister.readCacheDataFromFile(java.io.File) failed to verify: expected to be within a catch-all for an instruction where a monitor is heldAndroidObjectPersister.readCacheDataFromFile(java.io.File): [0xA]
E/dex2oat(29475): Verification failed on class com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister in /data/app/de.billiger.android-1/base.apk because: Verifier rejected class com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister due to bad method java.lang.Object com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister.readCacheDataFromFile(java.io.File)
E/dex2oat(29475): Rejecting class com.octo.android.robospice.persistence.springandroid.json.gson.GsonObjectPersister that attempts to sub-class erroneous class com.octo.android.robospice.persistence.springandroid.SpringAndroidObjectPersister in /data/app/de.billiger.android-1/base.apk

Does that information help you in any way? I didn't modify anything in the Persister-classes.

Did you have any similar error messages related to ART and dex2oat?

ajans commented 9 years ago

This issue in the Android tracker seems to be related, too: https://code.google.com/p/android/issues/detail?id=80961

It is maybe an issue with testcoverage being enabled during build and thus causing the jacoco-tool to not update the exception-tables correctly, thus the android-runtime discovers this error and complains about it.

How could I disable the testcoverage for a build? In Gradle, the corresponding directive is called "testCoverageEnabled".

As one commenter pointed out: Confirmed Removing testCoverageEnabled true from the Gradle file prevents the VerifyError.

ajans commented 9 years ago

After revisiting this issue and some experimenting, I dug into the specific error "SpringAndroidObjectPersister.readCacheDataFromFile(java.io.File) failed to verify: expected to be within a catch-all for an instruction where a monitor is held".

The mentioned method has a weird try-catch-logic as it catches FileNotFoundException, which is never thrown here, instead of IOException, and also it re-catches CacheLoadingException to give it precedence relative to the generic Exception-catch.

I simplified this try-catch-construct to only catch IOException for FileUtils.readFileToString and re-throw it wrapped in CacheLoadingException. If the cachefile is empty, the CacheLoadingException is also thrown as usual.

The fix is a change made against the latest release-tag robospice-parent-1.4.14 in the extension robospice-spring-android (as I had some trouble building on current master). Is it okay for you to get the Pull Request with this base? Or do you want the PR as a fix-branch to be rebased against master?

ajans commented 9 years ago

I can confirm the SpringAndroidObjectPersister delivers cache-results on Android 5.0 with ART-runtime with this patch again.

stephanenicolas commented 9 years ago

Thx Arne for the PR, we will look into it shortly now. Sorry for the delay, I had real xmas holidays, even a bit extended due to an injury at the right hand. But we are back ;)

deinlandel commented 9 years ago

Any updates on this one?