AppWidget refresh Uri for RemoteViews

2019-03-02 06:21发布

问题:

I have created an Appwidget that displays an image file (test.png) that is provided to it's RemoteViews via Uri. In onUpdate i run a service that changes the content of the file. I have also set an onClickListener for the image that will call onUpdate.

-If I create an instance of the AppWidget it displays the most recently changed version of the Uri file.

-If I click the widget, my service makes the approporaite changes to the file (which I can verify with a file explorer), but it does not update the image displayed in the AppWidget.

-(and most importantly)If I delete the AppWidget and create a new one, It displays the current/correct version of the image file.

I'm aware that my service may be taking too long to take effect on the first pass, but it should display the most recent image on the next onClick/call of onUpdate. As it stands now, the AppWidget only displays the version of the image file that exists on the first call of onUpdate.

Question:

What is the proper way to refresh the RemoteView content of an Appwidget, am I missing something in my Approach here?

thanks for your time!

Update:

I have tried calling the AppWidgetManager.notifyAppWidgetViewDataChanged() method from AppWidgetProvider.onReceive(), and still not change to the RemoteViews content after 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's 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:

There are various things that I have found can make widgets hard.

A. onUpdate isn't really an update mechanism

Contrary to how it sounds, onUpdate is only called in two situations:

  1. When the widget is created.
  2. Whenever the update cycle time (updateMillis defined in the xml definition file file for the widget) elapses.

Key point: onUpdate is never called at other times. (As far as I have ever seen in practice).

If you want the widget to update at another time, it is necessary to create a separate mechanism with knowledge of the widget and the capacity to be triggered. Typically this would be a Service which you start in the onUpdate routine. This might look like:

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!!
}

The service then sets the content sof the widget, and updates them as necessary, either through internal timekeeping or through the use of the Broadcast mechanism.

B. You can't really update a bit of the widget

It might seem logical to create a remoteViews when you create the widget and then update that, or the Views in it, when things change. In my experience this doesn't work predictably. When you want to change anything in a widget, create a new remoteViews, fill it out correctly and then assign it to the widget.



回答2:

I ran into some device dependency with the way RemoteView was handling URIs, and found my way to a solution like this:

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);

It also eliminated the need to cycle RemoteViews, as in my other answer.



回答3:

Certainly not elegant, or memory efficient, but I found that if I duplicated my RemoteViews

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

duplicate the code surrounding them, and swap them intermittently between image updates (kind of like a buffer), resetting the RemoteViews each time forces an update on their content!

Works for me for now, please feel free chime in with a more correct solution.