Widget onUpdate is not setting pendingIntent on button click after reboot

我是研究僧i 提交于 2019-12-05 19:45:26

Dynamically registering BroadcastReceivers in an AppWidgetProvider is a shaky solution, at best. AppWidgetProvider itself is a BroadcastReceiver, and instances of those statically registered in an app's manifest are meant to be rather short-lived.

However, since AppWidgetProvider is a BroadcastReceiver, we can take advantage of that, and simply target your TestWidget in the click PendingIntents. We can also attach the Widget ID as an extra to the Intent here, so we update the correct one when the click fires. For example:

Intent intent = new Intent(context, TestWidget.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
                                                         appWidgetId,
                                                         intent,
                                                         PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.appwidget_button, pendingIntent);

Note that we've also used the appWidgetId for the PendingIntent's requestCode. It's important that a distinct PendingIntent is used for each Widget instance, lest the wrong Widget instance be updated with the wrong extras. Using the already-unique Widget ID allows us to do that easily.

We then override TestWidget's onReceive() method, and check the Intent's action to determine if this is our click broadcast, or a normal Widget event broadcast from the system. In the example above, we didn't set an action, so we'll simply check for null here. However, you certainly could specify an action String, and it may be preferable to do so in some cases; e.g., if you have multiple Buttons in your Widget, and need to distinguish their click broadcasts.

@Override
public void onReceive(Context context, Intent intent) {
    if (intent.getAction() == null) {
        int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
        if (appWidgetId != -1) {
            updateWidgetText(context, appWidgetId, Math.random() + "");
        }
    }
    else {
        super.onReceive(context, intent);
    }
}

In the above, you can see that we pass the broadcast to the super method if we've found that it's not ours. AppWidgetProvider's onReceive() will then examine the Intent, and delegate to the appropriate event method, per usual.

Apart from being a stable solution, this approach has another upshot, in that a separate BroadcastReceiver instance does not need to be created, registered, and then unregistered for each Widget instance. Though we've added an onReceive() method, we can remove all of the dynamic BroadcastReceiver code, so our TestWidget class is still pretty short and simple.

public class TestWidget extends AppWidgetProvider {

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction() == null) {
            int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
            if (appWidgetId != -1) {
                updateWidgetText(context, appWidgetId, Math.random() + "");
            }
        }
        else {
            super.onReceive(context, intent);
        }
    }

    static void updateWidgetText(Context context, int appWidgetId, String newText) {
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.test_widget);
        views.setTextViewText(R.id.appwidget_text, newText);
        AppWidgetManager.getInstance(context).updateAppWidget(appWidgetId, views);
    }

    static void updateAppWidget(Context context, final AppWidgetManager appWidgetManager,
                                final int appWidgetId) {
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.test_widget);

        Intent intent = new Intent(context, TestWidget.class);
        intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
                                                                 appWidgetId,
                                                                 intent,
                                                                 PendingIntent.FLAG_UPDATE_CURRENT);
        views.setOnClickPendingIntent(R.id.appwidget_button, pendingIntent);

        appWidgetManager.updateAppWidget(appWidgetId, views);
    }

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }
}

this is the complete result of the above test project if anyone has similar problem. multiple buttons are added dynamically in configuration page and each has a user defined behavior. https://github.com/Alireza-Jamali/daily-task-checker

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!