AppWidget обновляет Uri для RemoteViews

Я создал Appwidget, который отображает файл изображения (test.png), который предоставляется ему RemoteViews через Uri. В onUpdate я запускаю службу, которая изменяет содержимое файла. Я также установил onClickListener для изображения, которое будет вызывать onUpdate.

-Если я создаю экземпляр AppWidget, он отображает последнюю измененную версию файла Uri.

-Если я нажимаю на виджет, моя служба делает соответствующие изменения в файле (который я могу проверить с помощью проводника файлов), но он не обновляет изображение, отображаемое в AppWidget.

- (и самое главное) Если я удалю AppWidget и создаю новый, он отобразит текущую/правильную версию файла изображения.

Я знаю, что мой сервис может занять слишком много времени, чтобы вступить в силу с первого прохода, но он должен отображать самое последнее изображение на следующем onClick/call onUpdate. В настоящее время AppWidget отображает только версию файла изображения, которая существует при первом вызове onUpdate.

Вопрос:

Каков правильный способ обновления содержимого RemoteView в Appwidget, я пропустил что-то в своем подходе здесь?

спасибо за ваше время!

Update:

Я попытался вызвать метод AppWidgetManager.notifyAppWidgetViewDataChanged() из AppWidgetProvider.onReceive() и по-прежнему не изменять содержимое RemoteViews после onUpdate().

public class CCWidgetProvider extends AppWidgetProvider {

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

    // Get all ids
    ComponentName thisWidget = new ComponentName(context,CCWidgetProvider.class);
    int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

    for (int widgetId : allWidgetIds)
        {
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout04);

        /*
         * it here that I run a service that changes the content of the file /test/test.png
         */

        RelativeLayout RL_widget = new RelativeLayout(context);
        LayoutInflater inflater = (LayoutInflater)context.getSystemService( Context.LAYOUT_INFLATER_SERVICE );
        RL_widget = (RelativeLayout)inflater.inflate(R.layout.widget_main, null);

        Uri uri = Uri.parse(Environment.getExternalStorageDirectory().getPath()+"/test/test.png");
        remoteViews.setImageViewUri(R.id.IV_widget_image,uri);


        Intent intent = new Intent(context, CCWidgetProvider.class);
        intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
        //PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        remoteViews.setOnClickPendingIntent(R.id.IV_widget_image, pendingIntent);

        appWidgetManager.updateAppWidget(widgetId, remoteViews);
    }
 }
} 

Ответ 1

Существуют различные вещи, которые я нашел, чтобы сделать виджеты трудными.

а. onUpdate на самом деле не является механизмом обновления

В отличие от того, как это звучит, onUpdate вызывается только в двух ситуациях:

  • Когда виджет создан.
  • Всякий раз, когда истекает время цикла обновления (updateMillis, определенное в файле файла определения xml для виджета).

Ключевые моменты: onUpdate никогда не вызывается в другое время. (Насколько я когда-либо видел на практике).

Если вы хотите, чтобы виджет обновлялся в другое время, необходимо создать отдельный механизм со знанием виджета и способностью запускаться. Обычно это будет Service, который вы запускаете в подпрограмме onUpdate. Это может выглядеть так:

public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds )
{       
    // start the service in case it ain't started yet, this also force a widget update to ensure correct status on them
    Intent intent = new Intent(context, MyService.class);
    intent.putExtra('service.startCode', /*A number to identify what is going on*/);
    context.startService(intent);

    // Everything else is triggered from the service!!
}

Затем служба устанавливает содержимое этого виджета и обновляет его по мере необходимости через внутренний хронометраж или с помощью механизма Broadcast.

В. Вы действительно не можете обновить немного виджета

Может показаться логичным создать remoteViews при создании виджета, а затем обновить его или просмотреть в нем, когда все изменится. По моему опыту это не работает предсказуемо. Когда вы хотите изменить что-либо в виджетах, создайте новый remoteViews, правильно заполните его и затем назначьте его виджету.

Ответ 2

Я столкнулся с некоторой зависимостью устройства с тем, как RemoteView обрабатывал URI, и нашел свой путь к решению вроде этого:

RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout);
Uri uri = Uri.parse("file://"+Environment.getExternalStorageDirectory().getPath()+"/test/test.png");

Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.crap);
bitmap = MediaStore.Images.Media.getBitmap(context.getContentResolver(), uri);
remoteViews.setImageViewBitmap(R.id.IV_widget_image, bitmap);

Это также устранило необходимость цикла RemoteViews, как и в моем другом ответе.

Ответ 3

Конечно, не изящный, а эффективный с точки зрения памяти, но я обнаружил, что если бы я дублировал свои RemoteViews

RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout01);
RemoteViews remoteViews2 = new RemoteViews(context.getPackageName(),R.layout.widget_layout02);

дублируйте код, окружающий их, и периодически меняйте их между обновлениями изображений (вроде буфера), каждый раз перезагрузка RemoteViews заставляет обновлять их содержимое!

Работает для меня сейчас, пожалуйста, не стесняйтесь говорить с более правильным решением.