Android App Widget列表不更新问题:为何仅标题可更新?
让我来帮你排查这个问题——核心原因是当应用未运行时,你的RemoteViewsFactory实例(以及它注册的广播接收器)可能已经被系统销毁了,导致广播无法触发ListView的数据更新,另外还有几个细节需要调整。下面是具体的解决方案和修改后的代码:
问题根源拆解
- 广播依赖不可靠:当应用进程被系统回收时,
WidgetRemoteViewsFactory里注册的广播接收器也会被销毁,此时发送的更新广播根本接收不到。 - PendingIntent配置问题:Android 12+对PendingIntent的安全性有强制要求,旧的
flag=0会导致PendingIntent在应用未运行时无法正确触发,甚至抛出异常。 - 未主动通知ListView刷新:你依赖广播触发数据更新,但系统提供了更可靠的
notifyAppWidgetViewDataChanged方法来通知RemoteViewsFactory刷新数据。
修改后的代码实现
1. 修正MyAppWidget.java
移除广播依赖,改用系统原生的刷新通知,同时修复PendingIntent的Flag配置:
public class MyAppWidget extends AppWidgetProvider { private static final String RELOAD_CLICKED = "MyAppWidget#btnReload"; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // 更新所有活跃的Widget for (int appWidgetId : appWidgetIds) { updateAppWidget(context, appWidgetManager, appWidgetId); } } @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); if (RELOAD_CLICKED.equals(intent.getAction())) { int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); updateAppWidget(context, appWidgetManager, appWidgetId); } } } static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) { WidgetViewModel viewModel = DbManager.getInstance(context).getRandomWidget(); RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.my_app_widget); // 更新标题(这部分你原来的逻辑是对的) views.setTextViewText(R.id.txtTitle, viewModel.title); // 配置RemoteAdapter,不再通过Intent传递数据,由Factory自行获取最新数据 Intent intent = new Intent(context, WidgetService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); views.setRemoteAdapter(R.id.widgetListTexts, intent); // 适配Android 12+的PendingIntent Flag,用appWidgetId作为requestCode避免多个Widget冲突 views.setOnClickPendingIntent(R.id.btnReload, getPendingSelfIntent(context, RELOAD_CLICKED, appWidgetId)); // 先更新Widget静态视图,再主动通知ListView刷新数据 appWidgetManager.updateAppWidget(appWidgetId, views); appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widgetListTexts); } private static PendingIntent getPendingSelfIntent(Context context, String action, int appWidgetId) { Intent intent = new Intent(context, MyAppWidget.class); intent.setAction(action); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); // Android 12+必须指定不可变/可变标志,这里用FLAG_IMMUTABLE(不需要修改Intent内容时) int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; return PendingIntent.getBroadcast(context, appWidgetId, intent, flags); } }
2. 重构WidgetRemoteViewsFactory.java
移除广播接收器,改用onDataSetChanged回调获取最新数据:
public class WidgetRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private final Context context; private final int appWidgetId; private String[] texts; public WidgetRemoteViewsFactory(Context context, Intent intent) { this.context = context; this.appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); // 初始化时加载最新数据 loadLatestData(); } // 新增方法:从数据库获取最新的文本数据 private void loadLatestData() { WidgetViewModel viewModel = DbManager.getInstance(context).getRandomWidget(); texts = new String[]{viewModel.widgetText1, viewModel.widgetText2}; } @Override public int getCount() { return texts != null ? texts.length : 0; } @Override public long getItemId(int position) { return position; } @Override public RemoteViews getLoadingView() { return null; } @Override public RemoteViews getViewAt(int position) { if (texts == null || position >= texts.length) { return null; } RemoteViews remoteView = new RemoteViews(context.getPackageName(), position == 0 ? R.layout.app_widget_textview1 : R.layout.app_widget_textview2); remoteView.setTextViewText( position == 0 ? R.id.widgetText1 : R.id.widgetText2, texts[position]); return remoteView; } @Override public int getViewTypeCount() { return 2; } @Override public boolean hasStableIds() { return true; // 改为true,position作为稳定的ID } @Override public void onCreate() { } @Override public void onDataSetChanged() { // 系统通知数据变化时,重新加载最新数据 loadLatestData(); } @Override public void onDestroy() { // 无需再注销广播,已经移除相关逻辑 } }
3. WidgetService.java保持不变
public class WidgetService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new WidgetRemoteViewsFactory(this.getApplicationContext(), intent); } }
为什么这样修改能解决问题?
- 移除广播依赖:
onDataSetChanged是系统保证会调用的回调,只要调用notifyAppWidgetViewDataChanged,不管应用是否运行,系统都会触发Factory刷新数据。 - 修复PendingIntent:指定符合Android 12+要求的Flag,同时用
appWidgetId作为requestCode,避免多个Widget的PendingIntent互相覆盖。 - 主动触发ListView刷新:在更新Widget静态视图后,调用
notifyAppWidgetViewDataChanged,强制系统更新ListView的数据。
内容的提问来源于stack exchange,提问作者Eng1234
相关产品推荐
相关产品推荐

