Android: Как заставить обновление всех виджетов определенного вида

Я видел много вопросов в этом направлении, и я продолжаю видеть один и тот же ответ снова и снова. Я не хочу иметь Сервис для каждого вида виджета, которое имеет мое приложение, особенно потому, что у моего приложения уже есть 2 постоянных сервиса.

В частности, если одна из моих существующих служб увидела, что данные изменились, я хочу обновить свои виджеты. Выполнение этого по таймеру раздражает, так как это может быть дни между обновлениями или может быть несколько в течение одного часа. Я хочу, чтобы мои виджеты ВСЕГДА отображали текущую информацию.

Дизайн виджета Android, похоже, работает на том основании, что ваш виджет извлекает информацию, когда он этого хочет, я думаю, что существует множество разумных сценариев, в которых действие может потребовать передачи данных в виджет.

Прочтите нижеприведенный ответ, как я разработал, как это сделать. Насколько я вижу, нет никаких побочных эффектов, если это сделано правильно.

Ответ 1

Чтобы наши виджеты для конкретного поставщика виджета были обновлены, нам нужно будет сделать следующее:

  • Настройте наш провайдер для получения специализированной широковещательной рассылки
  • Отправьте специализированную трансляцию с помощью:
    • Все текущие идентификаторы виджетов, связанные с провайдером
    • Данные для отправки
    • Ключи, на которые реагирует только наш поставщик (важно!)
  • Получение нашего виджета для обновления по клику (без сервиса!)

Шаг 1 - Настройте наш AppWidgetProvider

Я не буду подробно описывать создание xml файла info или изменений в манифесте Android. Если вы не знаете, как правильно создать виджет, то есть много учебников, которые вы должны прочитать в первую очередь.

Вот пример AppWidgetProvider Класс:

public class MyWidgetProvider extends AppWidgetProvider {

public static final String WIDGET_IDS_KEY ="mywidgetproviderwidgetids";
public static final String WIDGET_DATA_KEY ="mywidgetproviderwidgetdata";

}

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.hasExtra(WIDGET_IDS_KEY)) {
        int[] ids = intent.getExtras().getIntArray(WIDGET_IDS_KEY);
        if (intent.hasExtra(WIDGET_DATA_KEY)) {
           Object data = intent.getExtras().getParcelable(WIDGET_DATA_KEY);
           this.update(context, AppWidgetManager.getInstance(context), ids, data);
        } else {
            this.onUpdate(context, AppWidgetManager.getInstance(context), ids);
        }
    } else super.onReceive(context, intent);
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
        int[] appWidgetIds) {
    update(context, appWidgetManager, appWidgetIds, null);
}

//This is where we do the actual updating
public void update(Context context, AppWidgetmanager manager, int[] ids, Object data) {

    //data will contain some predetermined data, but it may be null
   for (int widgetId : ids) {
        .
        .
        //Update Widget here
        .
        .
        manager.updateAppWidget(widgetId, remoteViews);
    }
}

Шаг 2 - Отправка широковещательной рассылки

Здесь мы можем создать статический метод, который позволит обновить наши виджеты. Важно, чтобы мы использовали наши собственные ключи с действием обновления виджета, если мы используем AppWidgetmanager.EXTRA_WIDGET_IDS, мы не только сломаем собственный виджет, но и другие.

public static void updateMyWidgets(Context context, Parcelable data) {
    AppWidgetManager man = AppWidgetManager.getInstance(context);
    int[] ids = man.getAppWidgetIds(
            new ComponentName(context,MyWidgetProvider.class));
    Intent updateIntent = new Intent();
    updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
    updateIntent.putExtra(MyWidgetProvider.WIDGET_ID_KEY, ids);
    updateIntent.putExtra(MyWidgetProvider.WIDGET_DATA_KEY, data);
    context.sendBroadcast(updateIntent);
}

Если использовать этот метод с несколькими провайдерами, убедитесь, что они используют разные ключи. В противном случае вы можете увидеть виджет виджета с обновлением кода b и может иметь некоторые странные последствия.

Шаг 3 - обновление при нажатии

Еще одна приятная вещь - заставить наш виджет обновлять магически каждый раз, когда нажимается. Добавьте следующий код в метод обновления, чтобы добиться этого:

RemoteViews views = 
      new RemoteViews(context.getPackageName(),R.layout.mywidget_layout);

Intent updateIntent = new Intent();
updateIntent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateIntent.putExtra(myWidgetProvider.WIDGET_IDS_KEY, ids);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
      context, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT);

views.setOnClickPendingIntent(R.id.view_container, pendingIntent);

Этот код вызовет метод onUpdate при каждом щелчке виджета.

Примечания

  • Любой вызов updateWidgets() приведет к обновлению всех экземпляров нашего виджета.
  • Щелчок по виджету приведет к обновлению всех экземпляров нашего виджета
  • НЕТ СЕРВИСА НЕОБХОДИМА
  • Конечно, будьте осторожны, чтобы не обновлять часто - обновления виджета используют энергию аккумулятора.
  • Имейте в виду, что широковещательная передача принимается всеми поставщиками виджетов и ее специальными ключами, которые гарантируют, что на самом деле обновляются только наши виджеты.

Ответ 2

Извините за OP за поздний ответ, но для кого-либо еще вы можете использовать такой способ, чтобы обновить все виджеты вашего приложения (макет и класс):

public static void updateAllWidgets(final Context context,
                                    final int layoutResourceId,
                                    final Class< ? extends AppWidgetProvider> appWidgetClass)
{
    RemoteViews remoteViews = new RemoteViews(context.getPackageName(), layoutResourceId);

    AppWidgetManager manager = AppWidgetManager.getInstance(context);
    final int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, appWidgetClass));

    for (int i = 0; i < appWidgetIds.length; ++i)
    {
        manager.updateAppWidget(appWidgetIds[i], remoteViews);
    }
}

Что это значит, он создает RemoteViews для вашего макета, а затем получает трюк AppWidgetManager и находит все виджеты приложений для предоставленного вами класса, выполняет итерации над ними и обновляет их.

Ответ 3

В любое время, когда вы имеете дело с типами, довольно легко просто работать со статикой. И, если вы хотите, чтобы определенные вещи происходили на основе "триггера", я всегда рекомендовал программирование на основе событий... Намерения по сути являются "событием" по умолчанию в Android (для передачи данных в вашем мероприятии убедитесь, что объект реализует интерфейс Parcelable).

Существует много примеров этих концепций на различных форумах и документации SDK.