Android AppWidget有时不更新 问题

Android AppWidget有时不更新 问题,android,alarmmanager,android-appwidget,Android,Alarmmanager,Android Appwidget,我的AppWidget没有在应该更新的时候更新。我不明白为什么 相关代码 这是我的app\u widget\u info.xml文件updatePeriodMillis设置为零,因为我使用AlarmManager手动更新小部件: <?xml version="1.0" encoding="utf-8"?> <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" andr

我的
AppWidget
没有在应该更新的时候更新。我不明白为什么

相关代码 这是我的
app\u widget\u info.xml
文件
updatePeriodMillis
设置为零,因为我使用
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" />
以下是我的
服务
课程的相关部分:

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);
    }
}
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;
    }
}
我知道的和我尝试过的
  • update\u服务
    工作正常,每15分钟可靠地更新一次小部件
  • 正在设置
    service1
    service2
    的警报 下面是运行adb shell dumpsys alarm的一个片段,显示了其中一个重复报警(每15分钟或900000ms触发一次):

    下面是另一个显示在精确时间触发的警报:

    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}}
    
    以下是已触发警报的统计信息,其中
    d
    c
    指已启动的服务:

    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}
    
    如您所见,正在触发报警,但接口未更新

  • 由于重复警报每15分钟可靠地触发一次,我认为操作系统不会因为每30分钟只更新一次小部件而杀死它们
  • 我已尝试为每个报警创建不同的
    服务
    类,以避免报警按原样相互取消
  • 我使用了
    Log
    语句来确认在触发每个报警时是否实际调用了
    updateMyWidget()
    函数。似乎调用了该函数,但并不总是更新小部件
  • 我已尝试为每个
    pendingent
    设置不同的唯一
    requestCode
    s,以便在设置新报警时,不会无意中取消其他报警设置的
    pendingent
    s
  • 问题和想法 我做错了吗?平均而言,更新似乎只需要大约200毫秒,所以我可能应该使用
    pendingent.getBroadcast()
    并调用
    android.appwidget.action.appwidget\u UPDATE
    而不是创建
    服务来处理更新。这让我快发疯了,所以任何可能为我指明正确方向的提示或线索都将不胜感激

    更新 @Karakuri的回答教会了我如何区分
    pendingent
    对象,以避免它们在设置报警时相互覆盖。下面是我如何修复此代码的:

  • 我将
    putExtra()
    调用更改为
    setAction()
    ,并将该操作设置为预定义的常量之一,例如
    Intent.action\u MAIN
    ,该常量在语义上与服务所做的操作类似
  • 我从类级别的作用域中删除了所有服务,因为事实证明,在调用
    onUpdate()
  • 我去掉了
    onUpdate()中的所有条件逻辑
  • 我继续使用内置的android更新机制,即将
    app\u widget\u info.xml
    中的
    android:updatePeriodMillis
    更改为
    1800000
    (30分钟),并摆脱了我用
    setRepeating()设置的重复警报
  • 我发现我的报警边界条件存在问题,阻止了UI在我认为应该更新的时候进行更新——但是,如果我没有开始使用
    setAction()

  • 非常感谢

    以下是我的建议,希望在这里的某个地方能解决您的问题

  • 不要使用
    ==
    来比较服务(或任何地方)中的字符串。对命名常量使用
    .equals()

  • 您的
    pendingent
    s仅因附加内容不同而不同。当您将相同的
    意图
    赋予
    挂起内容
    时,它只保留其中一个。为了比较
    意图
    s,不考虑额外费用
    (有关此信息,请参阅
    pendingent
    的文档)。我会给每个
    Intent
    一个不同的操作,并在您的服务中使用
    Intent.getAction()
    ,而不是使用额外的操作

  • 不要依赖静态变量来保持状态。您的进程可以随时关闭,您将丢失这些数据。您的AppWidget正在另一个进程中运行,因此,除非您运行其他程序,否则Android可能会决定清除您的进程。我会使用某种持久性存储,可能是
    SharedPreferences
    ,如果它只是为了几个键的话


  • 这里是我的建议,希望在这里的某个地方能解决你的问题

  • 不要使用
    ==
    来比较服务(或任何地方)中的字符串。对命名常量使用
    .equals()

  • 您的
    pendingent
    s仅因附加内容不同而不同。当您将相同的
    意图
    赋予
    挂起内容
    时,它只保留其中一个。为了比较
    意图
    s,不考虑额外费用(有关此信息,请参阅
    pendingent
    的文档)。我会给每个
    Intent
    一个不同的操作,并在您的服务中使用
    Intent.getAction()
    ,而不是使用额外的操作

  • 不要依赖静态变量来保持状态。您的进程可以随时关闭,您将丢失这些数据。您的AppWidget正在另一个进程中运行,因此,除非您运行其他程序,否则Android可能会决定清除您的进程。我会使用某种持久性存储,可能是
    SharedPreferences
    ,如果它只是为了几个键的话


  • 这里是我的建议,希望在这里的某个地方能解决你的问题

  • 不要使用
    ==
    来比较服务(或任何地方)中的字符串。使用
    .equals()
    
    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}