Domanda

I'm working in my Android project developing with a HTC Desire (Gingerbread 2.3.7) and a Google Nexus 7 (Jelly Bean 4.3). I need to send some data from MainActivity to InfoActivity, so I use an intent. In this InfoActivity, I also have a menu item in the action bar to refresh the info.

enter image description here

In InfoActivity I show the data to the user. But this is not the problem, the problem is with the menu. Look at the following code:

public class ShowInfoActivity extends ActionBarActivity {

    private MenuItem menuItem   = null;
    // ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        new OneTask().execute(...);
        // ...
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        case R.id.refresh:
            menuItem = item;
            return true;
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private class OneTask extends AsyncTask<Object, Void, String> {
        // ...
        @Override
        protected void onPreExecute() {
            MenuItemCompat.setActionView(menuItem,
                        R.layout.actionbar_indeterminate_progress);
            MenuItemCompat.expandActionView(menuItem);
        }
        // ...
        @Override
        protected void onPostExecute(String result) {
            MenuItemCompat.collapseActionView(menuItem);
            MenuItemCompat.setActionView(menuItem, null);
        }
    }

Obviously, the first time it's executed, menuItem=null, so it must crash. Incredibly, in HTC it works fine but in Nexus it obviously crashes. Why is this different between devices?

PS: I already solved it, but I want to know why this behaviour...

È stato utile?

Soluzione

When in doubt, always check the source code. If you look at MenuItemCompat.java you'll find that it switches based on the API level like so:

    static final MenuVersionImpl IMPL;
    static {
        final int version = android.os.Build.VERSION.SDK_INT;
        if (version >= 14) {
            IMPL = new IcsMenuVersionImpl();
        } else if (version >= 11) {
            IMPL = new HoneycombMenuVersionImpl();
        } else {
            IMPL = new BaseMenuVersionImpl();
        }
    }

The base setActionView method for the base implementation (which is used for 2.3 devices) just returns the MenuItem, so it wouldn't ever throw the exception:

@Override
public MenuItem setActionView(MenuItem item, View view) {
    return item;
}

The HoneycombMenuVersionImpl, on the other hand, delegates to another class:

        @Override
        public boolean setShowAsAction(MenuItem item, int actionEnum) {
            MenuItemCompatHoneycomb.setShowAsAction(item, actionEnum);
            return true;
        }

And the delegate class attempts to call the actual method on the MenuItem, which will throw an exception:

public static void setShowAsAction(MenuItem item, int actionEnum) {
    item.setShowAsAction(actionEnum);
}

In this particular example, checking the source code answers your question and shows you a solid strategy for dealing with compatibility across different versions of Android.

Altri suggerimenti

setActionView()/collapseActionView() are native functions in Android 4 ... passing null MenuItem will surely be frowned upon.

On your Android 2.3 device, the compatibility library (android.support.v4) has it's own internal implementations of setActionView()/collapseActionView() that are likely more resilient to bad input.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top