The widget is rather simple, it displays the current temperature in Celsius and Fahrenheit simultaneously and allows you to click on it if you want to share the current temperature with your favourite social media site. Everything seemed to be working fine until I tried installing multiple instances of the widget. At the point, clicking on any widget would always share the weather of the last installed widget. I figured that I had a logic problem in my code and poured over it repeatedly. Nothing.
Here is the original erroneous code :
// Create the Intent that will be sent when the widget is clicked on
Intent intent = new Intent(this, BiPolar.class);
intent.setAction(CLICK_ACTION);
intent.putExtra(CLICK_WIDGET_ID, appWidgetId);
// Create the PendingIntent for the RemoteViews
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
//attach an on-click listener to the widget
views.setOnClickPendingIntent(R.id.root, pendingIntent);
From the documentation :
A PendingIntent itself is simply a reference to a token maintained by the system describing the original data used to retrieve it. This means that, even if its owning application's process is killed, the PendingIntent itself will remain usable from other processes that have been given it. If the creating application later re-retrieves the same kind of PendingIntent (same operation, same Intent action, data, categories, and components, and same flags), it will receive a PendingIntent representing the same token if that is still valid, and can thus call
cancel()
to remove it.
Because of this behavior, it is important to know when two Intents are considered to be the same for purposes of retrieving a PendingIntent. A common mistake people make is to create multiple PendingIntent objects with Intents that only vary in their "extra" contents, expecting to get a different PendingIntent each time. This does not happen. The parts of the Intent that are used for matching are the same ones defined by
Intent.filterEquals
. If you use two Intent objects that are equivalent as per Intent.filterEquals
, then you will get the same PendingIntent for both of them.The Solution
So how to we make sure that the requested PendingIntent is different from the existing ones? The solution is rather easy. Again from the documentation :
you will need to ensure there is something that is different about them to associate them with different PendingIntents. This may be any of the Intent attributes considered by
Intent.filterEquals
, or different request code integers supplied to getActivity(Context, int, Intent, int)
, getActivities(Context, int, Intent[], int)
, getBroadcast(Context, int, Intent, int)
, or getService(Context, int, Intent, int)
.
In my case the fix was very easy, just add the appWidgetId as the request code to the parameters of getBroadcast.
// Create the PendingIntent for the RemoteViewsPendingIntent pendingIntent = PendingIntent.getBroadcast(context, appWidgetId, intent, FLAG_UPDATE_CURRENT);
Although that field is not yet used, and may never be, it is enough for the system to distinguish the PendingIntent as being different from the other and to create a new one.