无法从广播接收器更新Android小部件

4

我希望编写一款应用程序,该程序可以在一个小部件中展示一些信息,并且可以定期更新。‘定期更新’指的是我使用了一个闹钟定时器来触发周期性更新。所以这就是问题所在:intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);对于广播接收器的意图来说是null。

以下是我的小部件提供者:

public class MyWidgetProvider extends AppWidgetProvider {
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        final int N = appWidgetIds.length;      
    for(int i=0; i<N; i++) {
            int widgetId = appWidgetIds[i];         
            RemoteViews rViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);         
            Intent intent = new Intent(context.getApplicationContext(), TrafficService.class);
            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);            
            rViews.setTextViewText(R.id.TextView01, "" + System.currentTimeMillis());           
            appWidgetManager.updateAppWidget(widgetId, rViews);
        }
    }
}

以下是导致问题的BroadcastReceiver:

public class TimeIntervalReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
// set new alarm timer (code cut out)       
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext());
        // PROBLEM BELOW!
        int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);     
        if(appWidgetIds == null) Log.d("TRAFFIC", "oh SHIT");
        if(appWidgetIds != null && appWidgetIds.length > 0) {
            for(int widgetId : appWidgetIds) {
                RemoteViews rViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
                rViews.setTextViewText(R.id.TextView01, "some data");
                appWidgetManager.updateAppWidget(widgetId, rViews);
            }
        }
    }
}

这个问题能解决吗?

4个回答

8
笑,我已经得到了答案...
只需替换:
int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);

by

ComponentName thisWidget = new ComponentName(getApplicationContext(),MyWidgetProvider.class);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);

0

当系统调用您的AppWidgetProvider实现时,它会使用这些额外信息填充意图。

但是,当它调用与小部件无关的您自己的广播接收器时,它不会在意图中填充此额外信息。

您将需要使用另一种方法来传输ID。也许您可以在闹钟触发时触发的Intent中填写它们?


0

所以我将广播接收器更改为以下内容:

public class TimeIntervalReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Calendar c = Calendar.getInstance();
        c.add(Calendar.SECOND, Config.UPDATE_RATE);     
        Date d = new Date(c.getTimeInMillis());

        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext());
        int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);

        if(appWidgetIds == null) Log.d("TRAFFIC", "oh SHIT");  // triggers :(
        if(appWidgetIds != null && appWidgetIds.length > 0) {
            for(int widgetId : appWidgetIds) {
                RemoteViews rViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
                rViews.setTextViewText(R.id.TextView01, "someupdateddata");
                appWidgetManager.updateAppWidget(widgetId, rViews);
            }
        }
        Intent i = new Intent(context, TimeIntervalReceiver.class);
        i.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); // here i'd add the existing widget ids...
        PendingIntent sender = PendingIntent.getBroadcast(context, 0, i, PendingIntent.FLAG_UPDATE_CURRENT);

        AlarmManager aManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        aManager.set(AlarmManager.RTC, d.getTime(), sender);
}
}

这仍然会触发 - 因为第一个闹钟定时器是在不知道小部件ID数组的情况下设置(例如,通过onclickhandler)... 有没有正确的方法来解决这个问题?


你应该编辑问题而不是发布另一个答案。无论如何,你能在应用程序小部件提供程序本身中设置第一个闹钟吗? - Jong
问题在于,第一个警报是在触发另一个事件时设置的,即在另一个广播接收器中。 - xenonite

0
想知道:为什么不直接覆盖WidgetProvider的onReceive()方法呢?由于AppWidgetProvider是从BroadcastReceiver扩展而来的,只要调用super.onReceive(),这是完全合法的。
通过onReceive()获取的Intent包含Widget Ids作为Extra,如果它是由AppWidgetHost(Launcher)调用的。如果您自己调用它,则必须自己添加所需的extras。
这看起来是一种优雅的方式,可以从任何其他活动触发WidgetProvider,同时保持其原始功能。
请记住:AppWidgetProvider是一个方便的类,易于开发小部件,但在核心上它只是一个BroadcastReceiver。
我是这样解决的:
public class WidgetProvider extends AppWidgetProvider {

    public static final String ACTION_RESTART_SERVICE = "ACTION_RESTART_SERVICE";

    @Override
    public void onReceive(Context context, Intent intent) {
    Log.d(TAG, "onEnabled() called.");
    if (intent.getAction() != null && intent.getAction().equals(WidgetProvider.ACTION_RESTART_SERVICE))
    {
        // Start service
        Log.d(TAG, "ACTION_RESTART_SERVICE... Restarting service with designated update interval...");
        Intent i = new Intent(context, UpdateWidgetService.class);
        service = startService(i, context, service);
    } else
    {
        // Other intent, call super class
        Log.d(TAG, "Not ACTION_RESTART_SERVICE... calling superclass onReceive()...");
        super.onReceive(context, intent);
    }
}
}

而在您的 Activity/Fragment 中:

    /**
 * Restart the update service via WidgetProvider to reflect new profile and settings
 * @param context Context is required
 */
private void restartService(Context context)
{
    Intent intent =  new Intent(context,
            WidgetProvider.class);
    intent.setAction(WidgetProvider.ACTION_RESTART_SERVICE);

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN)
    {
        // Send intents to all widget provider classes
        intent.setClass(context, WidgetProviderSize1.class);
        getActivity().sendBroadcast(intent);
        intent.setClass(context, WidgetProviderSize2.class);
        getActivity().sendBroadcast(intent);
        intent.setClass(context, WidgetProviderSize3.class);
        getActivity().sendBroadcast(intent);
        intent.setClass(context, WidgetProviderSize4.class);
        getActivity().sendBroadcast(intent);
        intent.setClass(context, WidgetProviderSize5.class);
        getActivity().sendBroadcast(intent);

    } else
        getActivity().sendBroadcast(intent);

}

看起来有点复杂,因为我有一个具有JellyBean动态可调整大小的小部件和低于该OS版本的固定小部件大小,但解决方案应该很清晰。

甚至更简单的解决方案可能是只需发送android.appwidget.action.APPWIDGET_UPDATE广播意图,就像启动器一样直接触发WidgetProvider的onUpdate()。

然后还有另一种完全不同的选择:让Updateservice自己获取WidgetIDs,因此无需从更新意图中获取它们。如果所有小部件基本上共享相同的配置,并且如果任何配置更改,则应更新所有小部件,则这是可以的:

    /**
 * Get all Widget IDs of WidgetProviders used by this app
 * @param appWidgetManager AppWidgetManager to use
 * @return Array of widget IDs
 */
private int[] getAppWidgetIDs(AppWidgetManager appWidgetManager)
{
    int[] widgetIdsOfOneProviderSize1 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize1.class);
    int[] widgetIdsOfOneProviderSize2 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize2.class);
    int[] widgetIdsOfOneProviderSize3 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize3.class);
    int[] widgetIdsOfOneProviderSize4 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize4.class);
    int[] widgetIdsOfOneProviderSize5 = getAppIdsOfSingleProvider(appWidgetManager, WidgetProviderSize5.class);
    int[] widgetIdsOfOneProvider = getAppIdsOfSingleProvider(appWidgetManager, WidgetProvider.class);
    int allWidgetIds[] = new int[widgetIdsOfOneProviderSize1.length + widgetIdsOfOneProviderSize2.length
                                 + widgetIdsOfOneProviderSize3.length + widgetIdsOfOneProviderSize4.length
                                 + widgetIdsOfOneProviderSize5.length + widgetIdsOfOneProvider.length];
    int index = 0;
    for (int id : widgetIdsOfOneProviderSize1)
    {
        allWidgetIds[index] = id;
        index ++;
    }
    for (int id : widgetIdsOfOneProviderSize2)
    {
        allWidgetIds[index] = id;
        index ++;
    }
    for (int id : widgetIdsOfOneProviderSize3)
    {
        allWidgetIds[index] = id;
        index ++;
    }
    for (int id : widgetIdsOfOneProviderSize4)
    {
        allWidgetIds[index] = id;
        index ++;
    }
    for (int id : widgetIdsOfOneProviderSize5)
    {
        allWidgetIds[index] = id;
        index ++;
    }

    for (int id : widgetIdsOfOneProvider)
    {
        allWidgetIds[index] = id;
        index ++;
    }

    return allWidgetIds;
}

private int[] getAppIdsOfSingleProvider(AppWidgetManager appWidgetManager, Class cls)
{
    ComponentName thisWidget = new ComponentName(getApplicationContext(),
            cls);
    int[] widgetIdsOfOneProvider = appWidgetManager.getAppWidgetIds(thisWidget);
    return widgetIdsOfOneProvider;
}

是的,我应该使用ArrayUtils将数组组合在一起... 这留下了改进的空间;-)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接