窗口小部件onUpdate未设置为挂起重启后单击按钮的意图 [英] Widget onUpdate is not setting pendingIntent on button click after reboot
问题描述
我正在创建一个测试小部件,通过单击其按钮显示随机数.一切都独立地位于我的Provider
的onUpdate
内部,包括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 BroadcastReceiver
s 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.
但是,由于AppWidgetProvider
是BroadcastReceiver
,因此我们可以利用它,只需在点击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 PendingIntent
s. 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
用于PendingIntent
的requestCode
.重要的是,每个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.
然后,我们重写TestWidget
的onReceive()
方法,并检查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 Button
s 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
方法. AppWidgetProvider
的onReceive()
然后将检查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屋!