The configuration bug

Fork me on GitHub

If you create a widget with a configuration activity, you may get a strange bug that involves unexpected creations of instances.

To clarify, lets recreate a use case.
First of all, edit our code, adding some more logs:
MaLuBuTestWidgetActivity.java

[...]
  public void mainOk(View source)
      {
      Log.d("Bug check", "MaLuBuTestWidgetActivity.mainOk()");
      [...]
      }
[...]

MaLuBuTestWidgetProvider.java

[...]
   @Override
   public void onReceive(Context context,
                         Intent intent)
      {
      Log.d("Bug check", "MaLuBuTestWidgetProvider.onReceive()");
      [...]
      }
   
   @Override
   public void onUpdate(Context context,
                        AppWidgetManager appWidgetManager,
                        int[] appWidgetIds)
      {
      [...]
      Log.d("Bug check", "MaLuBuTestWidgetProvider.onUpdate() - number of ids: " + allIds.length);
      [...]
      }
[...]

Then follow these steps:

  1. Pick up our widget and when the configuration activity appears, hit BACK.
  2. Pick up our widget again and this time add it.
  3. In the log, something strange will appear: it seems that onUpdate() is being called before we tap on the Ok button.


This is a known bug (Issue 3696): the onUpdate() of the widget is being called before the configuration activity is showed.
This bug can cause two problems:

  1. Even if we dismiss the configuration activity with BACK, the ID is saved and the number of instances increase.
  2. If we do some permanent changes in our onUpdate(), that changes take effect.

There is no real solution for this problem, so far.
Just pay attention to how to implement the onUpdate() and don’t call the super implementation of onReceive(), which is responsible of the strange onUpdate() call.
Be careful how you manage the AppWidgetManager.ACTION_APPWIDGET_UPDATE action in your onReceive(). The best way, in my opinion, is to use a custom action.
If you don’t call the super onReceive(), remember to rewrite the part that dispatch the other widget events:

else if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
            Bundle extras = intent.getExtras();
            if (extras != null && extras.containsKey(AppWidgetManager.EXTRA_APPWIDGET_ID)) {
                final int appWidgetId = extras.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID);
                this.onDeleted(context, new int[] { appWidgetId });
            }
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_ENABLED.equals(action)) {
            this.onEnabled(context);
        }
        else if (AppWidgetManager.ACTION_APPWIDGET_DISABLED.equals(action)) {
            this.onDisabled(context);
        }

Finally, don’t use the total number of instances; if it’s really necessary, save the “fake” ID each time is passed to onReceive().
That’s it! IF you missed the other posts, here is a list:

Take your time – Widgets and AlarmManager

One update for widget

Android App Widgets and their Configuration

Advertisements

About Luong
http://www.linkedin.com/in/manhluong

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s