The Problem
My AppWidget isn't updating when it should. I can't figure out why.
Relevant Code
Here is my app_widget_info.xml file. updatePeriodMillis is set to zero because I update the widget manually using the AlarmManager:
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="250dp" android:minHeight="110dp" android:updatePeriodMillis="0"
    android:initialLayout="@layout/app_widget"
    android:widgetCategory="home_screen|keyguard"
    android:initialKeyguardLayout="@layout/app_widget"
    android:previewImage="@drawable/preview_image" />
Here are the relevant portions of my AppWidgetProvider class:
public class MyAppWidget extends AppWidgetProvider {
    private static PendingIntent service1 = null, service2 = null, update_service = null;
    public static void nullifyService( String service_name ) {
        if ( service_name.equals( "service1" ) ) {
            service1 = null;
        } else {
            service2 = null;
        }
    }
    public static void updateMyWidget(final Context context, AppWidgetManager appWidgetManager, int appWidgetId) {
        /* ... code to update my widget ... */
        /* ... this code is tested and I know it works ... */
        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // set alarms if necessary for the beginning and end of the next void
        if ( null ==  update_service || ( null == service1 && null == service2 ) ) {
            final AlarmManager m = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
            // set alarms for the beginning and end of the upcoming/current event window
            if ( null == service1 && null == service1 ) {
                final Calendar s1_c = Calendar.getInstance(), // service1 alarm time
                               s2_c = Calendar.getInstance(); // service2 alarm time
                final Intent   s1_i = new Intent(context,MyService.class).putExtra( "serviceName", "service1" ),
                               s2_i = new Intent(context,MyService.class).putExtra( "serviceName", "service2" );
                final long s1_millis, s2_millis;
                // get the current VOC info
                if ( db == null ) db = new MyDataBase(context);
                alarm_info = db.getCurrentAlarms();
                // alarm times for service1 and service2 are UNIX timestamps
                // pulled from a sqlite database (tested and working fine)
                s1_c.setTime(new Date(alarm_info.getLong(4) * 1000 ));
                s2_c.setTime(new Date(alarm_info.getLong(1) * 1000 ));
                // if it is still before the next service1 alarm time
                // set an alarm for the exact time
                if ( s1_c.getTimeInMillis() > Calendar.getInstance().getTimeInMillis() ) {
                    s1_c.set(Calendar.MILLISECOND, 0);
                    s1_c.set(Calendar.SECOND, 0);
                    service1 = PendingIntent.getService(context, 0, s1_i, PendingIntent.FLAG_ONE_SHOT );
                    s1_millis = SystemClock.elapsedRealtime() + s1_c.getTime().getTime() - System.currentTimeMillis();
                    m.setExact(AlarmManager.ELAPSED_REALTIME, s1_millis, service1);
                }
                // set the alarm for the service2 alarm time
                s2_c.set(Calendar.MILLISECOND, 0);
                s2_c.set(Calendar.SECOND, 0);
                service2  = PendingIntent.getService(context, 1, s2_i, PendingIntent.FLAG_ONE_SHOT );
                s2_millis = SystemClock.elapsedRealtime() + s2_c.getTime().getTime() - System.currentTimeMillis();
                m.setExact(AlarmManager.ELAPSED_REALTIME, s2_millis, service2);
            }
            // set the widget to update every 15 minutes regardless
            if ( null == update_service ) {
                final Intent up_i = new Intent(context,MyService.class).putExtra( "serviceName", "update" );
                update_service = PendingIntent.getService(context, 2, up_i, PendingIntent.FLAG_CANCEL_CURRENT);
                m.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(), 15 * 60 * 1000, update_service);
            }
        }
        // There may be multiple widgets active, so update all of them
        for( int appWidgetId : appWidgetIds) {
            updateMyWidget(context, appWidgetManager, appWidgetId);
        }
    }
    @Override
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled
        final AlarmManager m = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
        m.cancel(service1);
        m.cancel(service2);
        m.cancel(update_service);
    }
}
And here are the relevant portions of my Service class:
public class MyService extends Service {
    @Override
    public int onStartCommand(Intent intent, int flags, int startId){
        // update all of the widgets
        ComponentName cn = new ComponentName(this, MyAppWidget.class);
        AppWidgetManager m = AppWidgetManager.getInstance(this);
        for ( int awid : m.getAppWidgetIds(cn) ) {
            MyAppWidget.updateMyWidget(this,m,awid);
        }
        String service_name = intent.getStringExtra( "serviceName" );
        if ( "service1" == service_name || "service2" == service_name ) {
            MyAppWidget.nullifyService(service_name);
        }
        return START_NOT_STICKY;
    }
}
What I Know and What I've Tried
- The update_serviceworks fine and updates the widget dependably every 15 minutes
- The alarms for service1andservice2are being set.
Here is a snippet from running adb shell dumpsys alarm showing one of the repeating alarms (that fires every 15 minutes, or 900000ms):
ELAPSED #0: Alarm{41a9e1b8 type 3 com.mycompany.myappwidget}
    type=3 whenElapsed=231265839 when=+11m52s295ms window=-1 repeatInterval=900000 count=0
    operation=PendingIntent{41dbf158: PendingIntentRecord{41cf7920 com.mycompany.myappwidget startService}}
And here is another showing one of the alarms that is set to fire at an exact time:
ELAPSED #4: Alarm{433d1560 type 3 com.mycompany.myappwidget}
    type=3 whenElapsed=90066209 when=+4h34m36s121ms window=0 repeatInterval=0 count=0
    operation=PendingIntent{41f0ec28: PendingIntentRecord{41f1e690 com.mycompany.myappwidget startService}}
And here are the stats for the alarms that have already fired where d and c refer to the services that got started:
com.mycompany.myappwidget +1s857ms running, 0 wakeups:
    +1s817ms 0 wakes 83 alarms: cmp={com.mycompany.myappwidget/com.mycompany.myappwidget.d}
    +40ms 0 wakes 1 alarms: cmp={com.mycompany.myappwidget/com.mycompany.myappwidget.c}
As you can see, the alarms are being triggered, but the interface is not updating.
- Since the repeating alarm fires reliably every 15 minutes, I don't think that the OS is killing them based on the restriction of only updating widgets every 30 minutes
- I have tried creating a different Serviceclass for each alarm in order to avoid the alarms canceling each other as is suggested by this post
- I have used Logstatements to confirm that theupdateMyWidget()function is actually being called when each alarm is triggered. It appears that the function is called, but doesn't always update the widgets.
- I have tried setting different unique requestCodes for eachPendingIntentso that when a new alarm is set, it doesn't inadvertently cancel thePendingIntents set by other alarms
Questions and Ideas
Am I doing this wrong? On average, updates only seem to take about 200ms, so perhaps I should be using PendingIntent.getBroadcast() and calling android.appwidget.action.APPWIDGET_UPDATE instead of creating a Service to handle the updates. This is driving me nuts, so any hints or clues that might point me in the right direction would be GREATLY appreciated.
UPDATE
@Karakuri's answer taught me how to differentiate PendingIntent objects for the purposes of not having them overwrite each other when setting alarms. Here's how I fixed this code:
- I changed the putExtra()calls tosetAction()and set the action as one of the predefined constants, e.g.Intent.ACTION_MAIN, that seemed semantically similar to what the service was doing
- I removed all of the services from the class-level scope, as it turns out it was unnecessary to keep them around in between calls to onUpdate()
- I got rid of all of the conditional logic inside of onUpdate()
- I went back to using the built-in android update mechanism, i.e. changed the android:updatePeriodMillisinapp_widget_info.xmlto1800000(30 minutes) and got rid of the repeating alarm I'd set withsetRepeating()
- I discovered that there was a problem with the boundary conditions for my alarms that was preventing the UI from updating when I thought it should--I wouldn't have discovered this, though, if I hadn't started using setAction()
Thank you very much!!!
 
     
    