14

Podczas wdrażania mojej aplikacji na Androida 1.6 napotkałem problem ze zgodnością wsteczną. Im coraz VerifyError na ten kawałek kodu:Instalacja VerifyError na API 1.6

if(android.os.Build.VERSION.SDK_INT >= 11) { 
    getActionBar().setBackgroundDrawable(getResources().getDrawable(R.drawable.actionbar_bg)); 
} 

To nie jest nieoczekiwane, ponieważ getActionBar() nie istnieje pre API 11, jednak po 1,6 (? API 5 i wyżej) buduje wszystko semi-wdzięcznie iść wokół tego zgodnie z komunikatem logcat otrzymanym podczas wdrażania, na przykład na urządzeniu poziomu API 8;

06-27 16:47:04.333: INFO/dalvikvm(11529): Could not find method com.me.app.MyActivity.getActionBar, referenced from method com.me.app.MyActivity.init 
06-27 16:47:04.333: WARN/dalvikvm(11529): VFY: unable to resolve virtual method 1090: Lcom.me.app.MyActivity;.getActionBar()Landroid/app/ActionBar; 
06-27 16:47:04.333: DEBUG/dalvikvm(11529): VFY: replacing opcode 0x6e at 0x004f 
06-27 16:47:04.333: DEBUG/dalvikvm(11529): VFY: dead code 0x0052-005f in Lcom.me.app.MyActivity;.init (Z)V 

1,6 a wcześniej przyzwyczajenie to zrobić, ale zamiast rzucać VerifyError:

06-27 16:23:45.561: ERROR/dalvikvm(427): Could not find method com.me.app.MyActivity.getActionBar, referenced from method com.me.app.MyActivity.init 
06-27 16:23:45.561: WARN/dalvikvm(427): VFY: unable to resolve virtual method 1090: Lcom/me/app/MyActivity;.getActionBar()Landroid/app/ActionBar; 
06-27 16:23:45.561: WARN/dalvikvm(427): VFY: rejecting opcode 0x6e at 0x004f 
06-27 16:23:45.561: WARN/dalvikvm(427): VFY: rejected Lcom/me/app/MyActivity;.init (Z)V 
06-27 16:23:45.561: WARN/dalvikvm(427): Verifier rejected class Lcom/me/app/MyActivity; 
06-27 16:23:45.561: WARN/dalvikvm(427): Class init failed in newInstance call (Lcom/me/app/MyActivity;) 
06-27 16:26:44.841: ERROR/AndroidRuntime(427): Uncaught handler: thread main exiting due to uncaught exception 
06-27 16:26:44.941: ERROR/AndroidRuntime(427): java.lang.VerifyError: com.me.app.MyActivity 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at java.lang.Class.newInstanceImpl(Native Method) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at java.lang.Class.newInstance(Class.java:1472) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.app.Instrumentation.newActivity(Instrumentation.java:1097) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2316) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2417) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.app.ActivityThread.access$2100(ActivityThread.java:116) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1794) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.os.Handler.dispatchMessage(Handler.java:99) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.os.Looper.loop(Looper.java:123) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at android.app.ActivityThread.main(ActivityThread.java:4203) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at java.lang.reflect.Method.invokeNative(Native Method) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at java.lang.reflect.Method.invoke(Method.java:521) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:791) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:549) 
06-27 16:26:44.941: ERROR/AndroidRuntime(427):  at dalvik.system.NativeStart.main(Native Method) 

czy istnieje sposób aby to naprawić elegancko i posiada wsteczną kompatybilność do 1,6?

Edit: Więc skończyło się tworząc klasę HoneycombHelper z metod statycznych:

public class HoneycombHelper { 
    public static void setActionBarBackgroundDrawable(Activity a, Drawable d) { 
     a.getActionBar().setBackgroundDrawable(d); 
    } 
    ... 
} 

Nie wiem, czy to jest najbardziej elegancki sposób, ale to nie wydają się działać.

Odpowiedz

24

Kiedy Dalvik kompiluje twoją klasę/funkcję z kodu bajtowego na natywny kod maszynowy, kompiluje wszystkie instrukcje, nawet te, które są w warunkach if. W maszynie wirtualnej Android 1.6 próbuje się rozwiązać (zweryfikować) funkcję getActionBar, a ponieważ nie ma takiej funkcji, Dalvik rzuca VerifyError.

Można zrobić kolejny trick:

class ActionBarHelper{ 
    void setBackground(){ 
     getActionBar().setBackgroundDrawable(...); 
    } 
} 

... 

if(android.os.Build.VERSION.SDK_INT >= 11) { 
    new ActionBarHelper().setBackground();  
} 

ten sposób ActioBarHelper klasa zostanie skompilowany tylko/weryfikowanym kiedy używasz na SDK 11+. To z kolei pozwoli na wywołanie funkcji getActionBar bez użycia refleksji (odbicie jest kolejnym możliwym rozwiązaniem tego problemu).

+0

powinna być statyczna klasa poprawnie? – schwiz