My app works perfectly on both emulated and real 1.6 devices. However The Sony Xperia X10 series (running 1.6) is crashing with a java.lang.VerifyError. This thread leads me to believe ProGuard optimization could be the culprit, but if that were the case wouldn't the problem exist on every device running 1.6, instead of just the X10?
Here is the stack trace:
java.lang.VerifyError: com.twocell.walkabout.Animator
at com.twocell.walkabout.Main.void onCreate(android.os.Bundle)(SourceFile:197)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2390)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2443)
at android.app.ActivityThread.access$2100(ActivityThread.java:117)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1815)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.app.ActivityThread.main(ActivityThread.java:4263)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:521)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:799)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:557)
at dalvik.system.NativeStart.main(Native Method)
Here is the code that triggers the error (in a class called 'Animator'):
static void setLayoutAnimation_textFadeIn(ViewGroup vg, Context context)
{
Animation text_fadeIn = AnimationUtils.loadAnimation(context, R.anim.text_fade_in);
LayoutAnimationController controller = new LayoutAnimationController(text_fadeIn, 0.25f);
vg.setLayoutAnimation(controller);
}
which is called from:
LinearLayout HeaderBar_Text = (LinearLayout) findViewById(R.id.HeaderBar_Text);
Animator.setLayoutAnimation_textFadeIn(HeaderBar_Text, this); // this is line 197
and here is text_fade_in.xml:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" android:fillAfter="true">
<alpha
android:fromAlpha="0.0"
android:toAlpha="1.0"
android:duration="1000"
/>
</set>
I don't have access to an X10 so I'm relying on stack traces delivered via ACRA.
EDIT (new info):
Ok, I was able to find an old emulator image of the X10 running 1.6 and re-create the problem. Turns out, it /is/ only crashing on the release (ProGuard) version. Turn off ProGuard and it works perfectly. Why on earth this only affects the X10 and not every single 1.6 device is the question. Here is some new info I've gathered since I have the emulator...
On some of my Activities I'm using overridePendingTransition
to change to a fading effect, however this is not supported on 1.6, so I'm using a helper class called VersionHelper:
class VersionHelper
{
static void overrideTransition(Context context)
{
((Activity)context).overridePendingTransition(R.anim.activity_fade_in, R.anim.activity_fade_out);
}
}
and it's called from Animator:
static void overrideTransition(Context context)
{
if (Build.VERSION.SDK_INT > 4)
{
VersionHelper.overrideTransition(context);
}
}
This allows 1.6 devices to run the rest of the code without crashing from the unsupported method (Thanks to Mark Murphy for this technique).
Now, the stack trace with extra log data:
07-19 23:42:29.150: ERROR/dalvikvm(705): Could not find method android.app.Activity.overridePendingTransition, referenced from method com.twocell.walkabout.Animator.overrideTransition
07-19 23:42:29.150: WARN/dalvikvm(705): VFY: unable to resolve virtual method 14: Landroid/app/Activity;.overridePendingTransition (II)V
07-19 23:42:29.150: WARN/dalvikvm(705): VFY: rejecting opcode 0x6e at 0x0012
07-19 23:42:29.150: WARN/dalvikvm(705): VFY: rejected Lcom/twocell/walkabout/Animator;.overrideTransition (Landroid/content/Context;)V
07-19 23:42:29.150: WARN/dalvikvm(705): Verifier rejected class Lcom/twocell/walkabout/Animator;
07-19 23:42:29.160: DEBUG/AndroidRuntime(705): Shutting down VM
07-19 23:42:29.160: WARN/dalvikvm(705): threadid=3: thread exiting with uncaught exception (group=0x4001aa28)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): Uncaught handler: thread main exiting due to uncaught exception
07-19 23:42:29.160: ERROR/AndroidRuntime(705): java.lang.VerifyError: com.twocell.walkabout.Animator
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at com.twocell.walkabout.Main.onCreate(SourceFile:199)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1123)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2364)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.app.ActivityThread.access$2100(ActivityThread.java:116)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.os.Handler.dispatchMessage(Handler.java:99)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.os.Looper.loop(Looper.java:123)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at android.app.ActivityThread.main(ActivityThread.java:4203)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at java.lang.reflect.Method.invokeNative(Native Method)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at java.lang.reflect.Method.invoke(Method.java:521)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549)
07-19 23:42:29.160: ERROR/AndroidRuntime(705): at dalvik.system.NativeStart.main(Native Method)
So there it is - when I use ProGuard, and only on the X10, it's still seeing overridePendingTransition
and crashing. (I confirmed that the X10 /is/ reporting "4" as its API version.)
Finally, my proguard.cfg:
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class com.android.vending.licensing.ILicensingService
-keep class com.android.vending.billing.**
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembers class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
#ACRA specifics
# we need line numbers in our stack traces otherwise they are pretty useless
-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable
# ACRA needs "annotations" so add this...
-keepattributes *Annotation*
# keep this class so that logging will show 'ACRA' and not a obfuscated name like 'a'.
# Note: if you are removing log messages elsewhere in this file then this isn't necessary
-keep class org.acra.ACRA {
*;
}
# keep this around for some enums that ACRA needs
-keep class org.acra.ReportingInteractionMode {
*;
}
# keep this otherwise it is removed by ProGuard
-keep public class org.acra.ErrorReporter
{
public void addCustomData(java.lang.String,java.lang.String);
}
# keep this otherwise it is removed by ProGuard
-keep public class org.acra.ErrorReporter
{
public org.acra.ErrorReporter$ReportsSenderWorker handleSilentException(java.lang.Throwable);
}
-keep class org.acra.ReportField {*;}
Any thoughts?
Edit 2
Using -dontoptimize
in ProGuard lets the app run on the X10. I'm sneaking up on a solution.
ProGuard's optimization step will inline methods like VersionHelper#overrideTransition(), which is not what you want, since the method is intended to isolate the dependency on 1.6 (although ProGuard can't know this).
Brute-force solution: switch off all optimizations:
Finer-grained: switch off all method inlining (as you've found out):
Ideal solution: avoid inlining this particular method and its siblings:
Without details, I can't say much about the other issues. You can ask questions on ProGuard's help forum.
The solution was to add
-optimizations !method/inlining/unique
to proguard.cfg, which according to ProGuard's docs normally "Inlines methods that are only called once". I wish I could say I figured that out without excluding every single optimization then removing them one by one as I built signed releases... but that's exactly what I did.Edit (new info)
As I continue working I'm discovering that there are other parts of my app that are crashing on the X10, and require other specific optimization exclusions to work properly. I'm also beginning to see a pattern with crash reports from other specific devices that simply should not be happening, and in fact don't happen on my own test devices and emulators.
I am forced to conclude that I cannot trust ProGuard's optimizations at this point. Since the problems only occur on specific devices, I'm more likely to place blame on manufacturer customizations, and not ProGuard. Finding the actual link between the two is beyond my capability.
So my final solution is to use
-dontoptimize
or risk getting 1-star reviews due to unsolvable problems. Neat. I've never been one to complain about platform fragmentation until now. This particular discovery is a serious disappointment.Edit 2 (followup)
Since I pushed an update removing the optimization outright, all crashing has stopped. Now I can finally get back to feature development.
Your
com.twocell.walkabout.Animator
class implementation is referring to some class or method that does not exist on the Xperia X10. It does not have to be limited to something in the staticsetLayoutAnimation_textFadeIn()
method -- it can be anything on that class. Unfortunately, theVerifyError
does not say what cannot be found.You will have to go through your application with a fine-toothed comb and determine what might be missing on the Xperia X10 that exists elsewhere.
Yes.