在午夜更新Android小工具

时间:2014-12-11 11:39:48

标签: android widget

如何在午夜每天更新Android小部件?下面的解决方案是通过查看有关此主题的堆栈溢出中的其他问题而形成的。

1)在扩展AppWidgetProvider的活动中创建窗口小部件时调用schedule(context)

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;
        schedule(context);
    super.onUpdate(context, appWidgetManager, appWidgetIds);

}

2)计划方法应设置计时器和警报,以便每天午夜更新小部件。但这不起作用。

protected void schedule(Context context) {
    final Intent i = new Intent(context, CalendarWidget.class);
    service = PendingIntent.getService(context, 0, i, PendingIntent.FLAG_CANCEL_CURRENT);

    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MINUTE, 0);
    calendar.set(Calendar.HOUR, 0);
    calendar.set(Calendar.AM_PM, Calendar.AM);
    calendar.add(Calendar.DAY_OF_MONTH, 1); 

    AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

//This doesn't work
       // alarmManager.set(AlarmManager.RTC, calendar.getTimeInMillis(), service);
//This doesn't work either.
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, service);
    }

解决方案不起作用;它很难测试,因为它只能在每天午夜进行测试;因此,赏金。我将把它授予第一个使其发挥作用的人。

2 个答案:

答案 0 :(得分:4)

我不久前创建了一个日期和时间小部件。以下是实施:

在AppWidgetProvider类中:

private boolean first_run = true;
public AlarmManager am;
public PendingIntent pi;

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    first_run = context.getSharedPreferences("mywidet", MODE_PRIVATE).getBoolean("first_run", true);
    if(first_run) {
        am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, AlarmManagerBroadcastReceiver.class);
        pi = PendingIntent.getBroadcast(context, 0, intent, 0);
        long current = System.currentTimeMillis();

        LocalTime localTime = LocalTime.now();
        long triggerOffset = (24*60*60 - localTime.getHour()*60*60 - localTime.getMinute()*60 - localTime.getSecond())*1000;
        am.setRepeating(AlarmManager.RTC_WAKEUP, current + triggerOffset, 86400000, pi);
        SharedPreferences.Editor editor = context.getSharedPreferences("mywidet", MODE_PRIVATE).edit();
        editor.putBoolean("first_run", false);
        editor.apply();
    }
    for (int appWidgetId : appWidgetIds) {
        appWidgetManager.updateAppWidget(appWidgetId, remoteView);
    }
}
在AlarmManagerBroadcasrReceiver类中

@Override
public void onReceive(Context context, Intent intent) {
    PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
    PowerManager.WakeLock wl = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "YOUR TAG");
    //Acquire the lock
    wl.acquire();

    Log.d("widget", "in alarm manager. static method will come next");

    ComponentName thisWidget = new ComponentName(context, NewAppWidget.class);
    AppWidgetManager manager = AppWidgetManager.getInstance(context);
    manager.updateAppWidget(thisWidget, remoteViews);
    //Release the lock
    wl.release();
}

编辑1

从API 19开始,所有setReapeating()警报都不准确。因此,人们可能希望执行以下操作:

if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, nexMin.getTimeInMillis()-current.getTimeInMillis(), 60000, pendingIntent);
} else {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, nexMin.getTimeInMillis() - current.getTimeInMillis(), pendingIntent);
}

您可以找到完整的实施here

答案 1 :(得分:2)

Rafiduzzman Sonnet's answer有效(但有点),但可以简化。 AppWidgetProvider已经是BroadcastReceiver,因此我们实际上可以将意图指向AppWidgetProvider,并覆盖onReceive来处理它。

在您的课程中AppWidgetProvider

private static final String ACTION_SCHEDULED_UPDATE = "your.package.name.SCHEDULED_UPDATE";

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction().equals(ACTION_SCHEDULED_UPDATE)) {
        AppWidgetManager manager = AppWidgetManager.getInstance(context);
        int[] ids = manager.getAppWidgetIds(new ComponentName(context, AppWidget.class));
        onUpdate(context, manager, ids);
    }

    super.onReceive(context, intent);
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
    // Do your updates...

    _scheduleNextUpdate(context);
}

private static void _scheduleNextUpdate(Context context) {
    AlarmManager alarmManager =
            (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    // Substitute AppWidget for whatever you named your AppWidgetProvider subclass
    Intent intent = new Intent(context, AppWidget.class);
    intent.setAction(ACTION_SCHEDULED_UPDATE);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);

    // Get a calendar instance for midnight tomorrow.
    Calendar midnight = Calendar.getInstance();
    midnight.set(Calendar.HOUR_OF_DAY, 0);
    midnight.set(Calendar.MINUTE, 0);
    // Schedule one second after midnight, to be sure we are in the right day next time this 
    // method is called.  Otherwise, we risk calling onUpdate multiple times within a few 
    // milliseconds
    midnight.set(Calendar.SECOND, 1);
    midnight.set(Calendar.MILLISECOND, 0);
    midnight.add(Calendar.DAY_OF_YEAR, 1);

    // For API 19 and later, set may fire the intent a little later to save battery,
    // setExact ensures the intent goes off exactly at midnight.
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
        alarmManager.set(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), pendingIntent);
    } else {
        alarmManager.setExact(AlarmManager.RTC_WAKEUP, midnight.getTimeInMillis(), pendingIntent);
    }
}

我们需要一个自定义意图操作,因为AppWidgetManager.ACTION_APPWIDGET_UPDATE不会更新所有小部件,即使您在AppWidgetManager.EXTRA_APPWIDGET_IDS下传递完整的ID数组也是如此。它只是更新创建的第一个小部件。

Here is a project I'm working on实现与此类似的计划小部件更新。