窗口小部件onUpdate未设置为挂起重启后单击按钮的意图 [英] Widget onUpdate is not setting pendingIntent on button click after reboot

查看:60
本文介绍了窗口小部件onUpdate未设置为挂起重启后单击按钮的意图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在创建一个测试小部件,通过单击其按钮显示随机数.一切都独立地位于我的ProvideronUpdate内部,包括pendingIntent.它可以正常工作,但是重新启动手机后,views.setOnClickPendingIntent不起作用,尽管重新创建了RemoteViews没问题,但是按钮没有响应.

I'm creating a test widget that shows random number by clicking its button. everything is inside onUpdate of my Provider independently, including the pendingIntent. it works fine but after rebooting the phone views.setOnClickPendingIntent is not working although RemoteViews is recreated with no issue but the button becomes unresponsive.

public class TestWidget extends AppWidgetProvider {
    static HashMap<Integer, BroadcastReceiver> br = new HashMap<>();

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

        BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                views.setTextViewText(R.id.appwidget_text, Math.random() + "");
                appWidgetManager.updateAppWidget(appWidgetId, views);
            }
        };
        br.put(appWidgetId, broadcastReceiver);//to unregister later

        Intent intent = new Intent("action");
        IntentFilter intentFilter = new IntentFilter("action");

        context.registerReceiver(broadcastReceiver, intentFilter);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 123, 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);
        }
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        for (int appWidgetId : appWidgetIds) {
            context.unregisterReceiver(br.get(appWidgetId));
        }
    }
}

清单:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.aeza.sta">
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <receiver android:name=".TestWidget" android:enabled="true" android:exported="false" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/test_widget_info" />
        </receiver>

        <activity android:name=".TestWidgetConfigureActivity">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
            </intent-filter>
        </activity>
    </application>

</manifest>

推荐答案

动态地在AppWidgetProvider中注册BroadcastReceiver是一个摇摇欲坠的解决方案. AppWidgetProvider本身是BroadcastReceiver,因此静态地注册在应用清单中的实例的寿命很短.

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.

但是,由于AppWidgetProviderBroadcastReceiver,因此我们可以利用它,只需在点击PendingIntent s中定位您的TestWidget.我们还可以在此处将Widget ID作为附加项附加到Intent上,以便在触发点击时更新正确的Widget ID.例如:

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

请注意,我们还将appWidgetId用于PendingIntentrequestCode.重要的是,每个Widget实例均应使用不同的PendingIntent,以免错误的Widget实例被错误的附加功能更新.使用已经唯一的Widget ID,我们可以轻松做到这一点.

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.

然后,我们重写TestWidgetonReceive()方法,并检查Intent的操作,以确定这是我们的单击广播还是从系统广播的常规Widget事件.在上面的示例中,我们未设置任何操作,因此我们仅在此处检查是否为null.但是,您当然可以指定操作String,在某些情况下最好这样做.例如,如果您的窗口小部件中有多个Button,并且需要区分其点击广播.

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

在上面,如果我们发现广播不是我们的广播,则可以看到我们将广播传递给了super方法. AppWidgetProvideronReceive()然后将检查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.

除了是一个稳定的解决方案之外,该方法还有另一个优势,因为不需要为每个Widget实例创建,注册和注销单独的BroadcastReceiver实例.尽管我们添加了onReceive()方法,但是我们可以删除所有动态BroadcastReceiver代码,因此我们的TestWidget类仍然非常简短.

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

这篇关于窗口小部件onUpdate未设置为挂起重启后单击按钮的意图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆