Sunday, October 14, 2012

Conversion to Dalvik format failed with error 1 (Dx local variable type mismatch)

Recently I got infamous "Conversion to Dalvik format failed with error 1" message while I was trying to import my own (obfuscated and optimized) JAR to one of my projects. The full message is quoted bellow:

EXCEPTION FROM SIMULATION:
[2012-10-09 18:50:04 - AnotherTest] Dx local variable type mismatch: attempt to set or access a value of type java.lang.Object using a local variable of type int. This is symptomatic of .class transformation tools that ignore local variable information.

[2012-10-09 18:50:04 - AnotherTest] Dx ...at bytecode offset 00000019
locals[0000]: Lbiz/binarysolutions/signature/CaptureBase;
locals[0001]: I
locals[0002]: Landroid/view/KeyEvent;
stack[0002]: Lbiz/binarysolutions/signature/CaptureBase;
stack[0001]: Lbiz/binarysolutions/signature/CaptureBase;
stack[top0]: Lbiz/binarysolutions/signature/CaptureBase;
...while working on block 0016
...while working on method onKeyDown:(ILandroid/view/KeyEvent;)Z
...while processing onKeyDown (ILandroid/view/KeyEvent;)Z
...while processing biz/binarysolutions/signature/CaptureBase.class

[2012-10-09 18:50:04 - AnotherTest] Dx 1 error; aborting
[2012-10-09 18:50:04 - AnotherTest] Conversion to Dalvik format failed with error 1
[2012-10-09 18:50:04 - AnotherTest] Dx 

Obviously, it was ProGuard's fault since importing the original JAR file went without any problems. Although the message (quoted part marked in bold) was cryptic at first, the clue that it happened in my onKeyDown method (also marked in bold) was enough to start digging.

So the original onKeyDown looked as following:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
  if (keyCode == KeyEvent.KEYCODE_BACK && event.getRepeatCount() == 0) {
    if (signatureView.isModified()) {
 Toast.makeText(this, getToastMessageID(), Toast.LENGTH_LONG)
          .show();
 return true;
    }
  }
return super.onKeyDown(keyCode, event);
}

I took the problematic JAR file processed by ProGuard, decompiled the CaptureBase.class file and took a look at onKeyDown again:
public boolean onKeyDown(int keyCode, KeyEvent event)
  {
    if ((keyCode == 4) && (event.getRepeatCount() == 0) && 
      (this.a.isModified())) {
      keyCode = this; 
      Toast.makeText(this, getResources().getIdentifier(
        "biz_binarysolutions_signature_ToastMessage", "string", 
        keyCode.getPackageName()), 1).show();
      return true;
    }
    return super.onKeyDown(keyCode, event);
  }
Well, now it all makes sense - for some 'optimization' reason, ProGuard assigned this (android.app.Activity) to keyCode (local variable of type int) and used it in getPackageName() call. Dalvik does not like it.

The fix would be the following: you need to add !code/allocation/variable string to your -optimizations command in ProGuard config file. Mine looks like the following:

-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*,!code/allocation/variable

And that should be it!

8 comments:

  1. AWESOME! Thx alot.

    ReplyDelete
  2. Wow, that solved my problem. Nothing else helped! I was just losing my hope. Thanks a lot!!!

    ReplyDelete
  3. Thanks so much for posting this! I had the exact same error with android.support.v4.app.FragmentManagerImpl and adding the suggested clause to my ProGuard configuration file fixed the error. I'm not sure I would have found this on my own.

    ReplyDelete
  4. it doesn't work for me could you please help me i am getting an error similar to yours
    Error:Uncaught translation error: com.android.dx.cf.code.SimException: local variable type mismatch: attempt to set or access a value of type byte[] using a local variable of type java.lang.String. This is symptomatic of .class transformation tools that ignore local variable information.

    ReplyDelete
    Replies
    1. Hello Islam, unfortunately I don't have time to look at your problem, but perhaps someone else could help you in comment section.

      Delete
  5. Islam,
    If you have found a solution I would love to hear it. Have the same error except the java.lang.String is replaced by variable that holds a callback method.

    ReplyDelete