2

I've tried to add the following class to my app:

public class AlertDialogHelper {

    public static AlertDialog.Builder getDarkDialogBuilder(Context context) {
        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
            int alertDialogTheme = AlertDialog.THEME_HOLO_DARK;

            if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                alertDialogTheme = AlertDialog.THEME_DEVICE_DEFAULT_DARK;
            }

            return new AlertDialog.Builder(context, alertDialogTheme);
        }

        return new AlertDialog.Builder(context);
    }

    public static AlertDialog getDeleteNoteDialog(Context context, OnClickListener deleteListener) {
        AlertDialog.Builder builder = new AlertDialog.Builder(context);

        builder.setMessage(R.string.dialog_delete_message);

        builder.setPositiveButton(R.string.button_delete, deleteListener);

        builder.setNegativeButton(R.string.button_cancel, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
            }

        });

        return builder.create();
    }

}

Whenever and wherever I call AlertDialogHelper.getDeleteNoteDialog(this, null) while running on Android 1.6, I get the following error:

03-28 18:56:07.828: E/dalvikvm(303): Could not find method android.app.AlertDialog$Builder.<init>, referenced from method net.ricardoamaral.apps.notificationagenda.AlertDialogHelper.getDarkDialogBuilder
03-28 18:56:07.828: W/dalvikvm(303): VFY: unable to resolve direct method 40: Landroid/app/AlertDialog$Builder;.<init> (Landroid/content/Context;I)V
03-28 18:56:07.828: W/dalvikvm(303): VFY:  rejecting opcode 0x70 at 0x0010
03-28 18:56:07.828: W/dalvikvm(303): VFY:  rejected Lnet/ricardoamaral/apps/notificationagenda/AlertDialogHelper;.getDarkDialogBuilder (Landroid/content/Context;)Landroid/app/AlertDialog$Builder;
03-28 18:56:07.828: W/dalvikvm(303): Verifier rejected class Lnet/ricardoamaral/apps/notificationagenda/AlertDialogHelper;

This works just fine on any other version above 1.6. To be honest I only tested this on 2.1, 2.3 and 4.0. I assume it also works on all others (it might not be true though).

If I comment the first method in the AlertDialogHelper class (the one the error is complaining about), the error goes way. But I need that method for other things and the error shows up anyway if I call that method too.

SOLUTION WITHOUT REFLECTION:

To fix the problem I've added the following class as nested-class to AlertDialogHelper:

private static class Compatibility {
    public static AlertDialog.Builder createAlertDialogBuilder(Context context, int alertDialogTheme) {
        return new AlertDialog.Builder(context, alertDialogTheme);
    }
}

Then, in the getDarkDialogBuilder method, instead of calling this:

return new AlertDialog.Builder(context, alertDialogTheme);

I call this:

return Compatibility.createAlertDialogBuilder(context, alertDialogTheme);

This is how I've been fixing similar problems and so far I haven't had any issues with this method.

Charles
  • 50,943
  • 13
  • 104
  • 142
rfgamaral
  • 16,546
  • 57
  • 163
  • 275
  • Depending on your app, I'd say it's safe to not worry about 1.6 – Phix Mar 28 '12 at 19:09
  • 1
    It is usually a good idea to actually show the failing call, if you are going to bother with a code listing. – CommonsWare Mar 28 '12 at 19:12
  • @Phix My app is released already and it supports 1.6. I don't have many users on this version, that's a fact, but I'd rather keep supporting it. – rfgamaral Mar 29 '12 at 00:31
  • @CommonsWare What do you mean? It's right there when I start with "Whenever and wherever I call...". Am I not understanding your question? – rfgamaral Mar 29 '12 at 00:32
  • That's a nice solution. It's very close to what Google recommends in the article [_How to have your (Cup)cake and eat it too](http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html). – Ted Hopp Mar 29 '12 at 04:56

2 Answers2

7

My guess is that inside getDarkDialogBuilder you are calling the two-argument constructor AlertDialog.Builder(Context context, int theme). That was introduced in API level 11. For earlier API levels, you only have the single-argument constructor available: AlertDialog.Builder(Context context).

As an aside, it would improve the quality of help that you get from this forum if you posted the relevant parts of your code. If the problem goes away when you comment out the method getDarkDialogBuilder, then you should post the entire source for the method.

Ted Hopp
  • 232,168
  • 48
  • 399
  • 521
  • I know OP said he tested on 2.1 and 2.3 and it worked, but I think Ted is right. Note the method the stack trace says couldn't be found: `Landroid/app/AlertDialog$Builder;. (Landroid/content/Context;I)` – kabuko Mar 28 '12 at 19:22
  • @kabuko - Exactly what led me to my answer. I wonder if perhaps the problematic method was not called when OP tested on 2.1 and 2.3. – Ted Hopp Mar 28 '12 at 19:26
  • @TedHopp Yes, I am calling the 2-argument constructor inside `getDarkDialogBuilder` (but I do have an `if` to see if I can use it). I didn't post the whole method because my testing made me believe it was irrelevant. I didn't seem to test it properly. – rfgamaral Mar 29 '12 at 00:38
  • @TedHopp **Q1)** I was fully aware that 2-argument constructor was only available on API Level 11 and above but since the code **only** failed on 1.6, I assumed that that was not problem. How come this only happens on 1.6 then? – rfgamaral Mar 29 '12 at 00:38
  • @TedHopp **Q2)** Why do I get the error when I only call `getDeleteNoteDialog` and not `getDarkDialogBuilder`? – rfgamaral Mar 29 '12 at 00:40
  • @RicardoAmaral - It's probably not enough to guard against calling the 2-arg constructor with a simple `if`; you usually need to use reflection to access the constructor safely from code that will also run on 1.6. The reasons for this and guidance on how to do it can be found in [this blog entry](http://android-developers.blogspot.com/2010/07/how-to-have-your-cupcake-and-eat-it-too.html) in the docs. Without seeing your code, I can't begin to address Q2. – Ted Hopp Mar 29 '12 at 00:46
  • @TedHopp Now that I know the problem, I've already fixed it (without reflection). But I just don't understand why do I need to fix it. The constructor only available on API Level 11 and above is the one causing problems. It should cause problems on every API Level below 11, but it only has problems in 1.6. Why? That's what I'm trying to understand. I have updated the code for Q2. – rfgamaral Mar 29 '12 at 00:54
  • @RicardoAmaral - I just noticed that the problem as described in Q2 (only `getDeleteNoteDialog` doesn't work) is inverted from the problem described in the original post (`getDarkDialogBuilder` doesn't work). Assuming that the original is correct, the answer is obvious: `getDeleteNoteBuilder` doesn't use the two-arg constructor. I'm curious how you fixed the problem; does the code you posted now work? As to why only a problem on 1.6: perhaps the class loader on 1.6 uses different rules for verifying .class files. – Ted Hopp Mar 29 '12 at 01:17
  • @TedHopp Maybe I didn't express myself right for Q2. The problem happens when I call **either method**. And as you correctly stated, `getDeleteNoteBuilder` does not use the 2-argument constructor. So why does it fail too? How I fixed the problem is a solution I actually took from another question here on StackOverflow from a similar problem (I didn't know it was a similar problem as this only happens on 1.6 and that got me confused, otherwise this question wouldn't exist). I'll post the solution code in the question. – rfgamaral Mar 29 '12 at 01:22
0

My guess is, the operation you are performing there is not supported in version 1.6 and introduced in the other versions you tested. Other way to validate this assumption is, go to javadocs for this method and see since:, it shows which version method was introduced.

kosa
  • 65,990
  • 13
  • 130
  • 167