You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android App Widget列表不更新问题:为何仅标题可更新?

让我来帮你排查这个问题——核心原因是当应用未运行时,你的RemoteViewsFactory实例(以及它注册的广播接收器)可能已经被系统销毁了,导致广播无法触发ListView的数据更新,另外还有几个细节需要调整。下面是具体的解决方案和修改后的代码:

问题根源拆解

  1. 广播依赖不可靠:当应用进程被系统回收时,WidgetRemoteViewsFactory里注册的广播接收器也会被销毁,此时发送的更新广播根本接收不到。
  2. PendingIntent配置问题:Android 12+对PendingIntent的安全性有强制要求,旧的flag=0会导致PendingIntent在应用未运行时无法正确触发,甚至抛出异常。
  3. 未主动通知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);
    }
}

为什么这样修改能解决问题?

  1. 移除广播依赖:onDataSetChanged是系统保证会调用的回调,只要调用notifyAppWidgetViewDataChanged,不管应用是否运行,系统都会触发Factory刷新数据。
  2. 修复PendingIntent:指定符合Android 12+要求的Flag,同时用appWidgetId作为requestCode,避免多个Widget的PendingIntent互相覆盖。
  3. 主动触发ListView刷新:在更新Widget静态视图后,调用notifyAppWidgetViewDataChanged,强制系统更新ListView的数据。

内容的提问来源于stack exchange,提问作者Eng1234

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:06:12