【问题标题】:Android widget buttons stop workingAndroid小部件按钮停止工作
【发布时间】:2011-07-13 20:09:10
【问题描述】:

我有一个带有小部件的 Android 应用程序,它有按钮。 此代码有效。

当发生某些事情时,小部件上的按钮停止工作,例如更改手机的语言。我使用共享首选项,因此如果用户重新安装应用程序(没有卸载),按钮将再次起作用并且设置保持不变。

  • 我注意到我的AppWidgetProvider 类(此分析下的代码)中的意图未正确触发。
  • 我向从AppWidgetProvider 实例化的Call1 类添加了一条Toast 消息,但它没有显示。
  • 我的UpdateService.java 只是获取设置的首选项并自定义小部件的外观,所以我认为这可能与我的问题无关。
  • 我的Main.java 文件仅包含微调器并保存共享首选项,这意味着我在微调器中选择“计算机”,这样“计算机”文本就会出现在小部件上。当我更改手机的语言时,它也不会消失,图像也不会消失。所以,我相信UpdateService.java一定没问题。

这里是 AppWidgetProvider 类:

public class HelloWidget extends AppWidgetProvider {

    public static String ACTION_WIDGET_CONFIGURE = "ConfigureWidget";
    public static String ACTION_WIDGET_CONFIGURE2 = "ConfigureWidget";
    public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
    public static String ACTION_WIDGET_RECEIVER2 = "ActionReceiverWidget";
    private static final int REQUEST_CODE_FOUR = 40;
    private static final int REQUEST_CODE_FIVE = 50;
    private static final int REQUEST_CODE_SIX = 60;
    private static final int REQUEST_CODE_SEVEN = 70;
    private static final int REQUEST_CODE_EIGHT = 80;

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        context.startService(new Intent(context, UpdateService.class));
        //Intent widgetUpdateIntent = new Intent(context, UpdateService.class);
        //context.startService(widgetUpdateIntent );

         RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain2);

         //P1 starts Call1.class
         Intent configIntent4 = new Intent(context, Call1.class);
         configIntent4.setAction(ACTION_WIDGET_CONFIGURE);
         PendingIntent configPendingIntent4 = PendingIntent.getActivity(context, REQUEST_CODE_FOUR, configIntent4, 0);
         remoteViews.setOnClickPendingIntent(R.id.ImageView01, configPendingIntent4);

         //P2 starts Call2.class
         Intent configIntent5 = new Intent(context, Call2.class);
         configIntent5.setAction(ACTION_WIDGET_CONFIGURE);
         PendingIntent configPendingIntent5 = PendingIntent.getActivity(context, REQUEST_CODE_FIVE, configIntent5, 0);
         remoteViews.setOnClickPendingIntent(R.id.ImageView02, configPendingIntent5);

         //P3 starts Call3.class
         Intent configIntent6 = new Intent(context, Call3.class);
         configIntent6.setAction(ACTION_WIDGET_CONFIGURE);
         PendingIntent configPendingIntent6 = PendingIntent.getActivity(context, REQUEST_CODE_SIX, configIntent6, 0);
         remoteViews.setOnClickPendingIntent(R.id.ImageView03, configPendingIntent6);

         //P4 starts Call4.class
         Intent configIntent7 = new Intent(context, Call4.class);
         configIntent7.setAction(ACTION_WIDGET_CONFIGURE);
         PendingIntent configPendingIntent7 = PendingIntent.getActivity(context, REQUEST_CODE_SEVEN, configIntent7, 0);
         remoteViews.setOnClickPendingIntent(R.id.ImageView04, configPendingIntent7);

         //P5 starts Call5.class
         Intent configIntent8 = new Intent(context, Call5.class);
         configIntent8.setAction(ACTION_WIDGET_CONFIGURE);
         PendingIntent configPendingIntent8 = PendingIntent.getActivity(context, REQUEST_CODE_EIGHT, configIntent8, 0);
         remoteViews.setOnClickPendingIntent(R.id.ImageView05, configPendingIntent8);

         appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
   }

    @Override
    public void onReceive(Context context, Intent intent) {

        final String action = intent.getAction();
        if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) 
        {
            final int appWidgetId = intent.getExtras().getInt(
                    AppWidgetManager.EXTRA_APPWIDGET_ID,AppWidgetManager.INVALID_APPWIDGET_ID);
            if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) 
            {
                this.onDeleted(context, new int[] { appWidgetId });
            }
        } 
        else 
        {
            if (intent.getAction().equals(ACTION_WIDGET_RECEIVER)) 
            {
                String msg = "null";
                try {
                    msg = intent.getStringExtra("msg");
                    } catch (NullPointerException e) {
                    //Log.e("Error", "msg = null");
                    }
            }
            super.onReceive(context, intent);
            }
    }
}

我还有一个EditPreferences.javaGlobalVars.java 和其他一些现在毫无意义的课程。类的名称不言自明。

另一件事。我也有Widgetmain.java

  public class WidgetMain extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.widgetmain2);
    }
    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager, int appWidgetId) 
    {
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetmain2);

          appWidgetManager.updateAppWidget(appWidgetId, remoteViews);

    }
}

编辑:这个怎么样:

当我在同事的 ZTE Blade 上安装此应用程序时,小部件上的文本视图未加载适当的文本,仅加载了 strings.xml 中确定的文本。

当我重新安装应用程序(不卸载)时,文本视图已加载并且一切正常。 这个问题在我的 HTC Desire HD 上没有出现。

文本视图像这样(部分代码)加载到上述 UpdateService.java 中:

RemoteViews updateViews = new RemoteViews(this.getPackageName(), R.layout.main);
updateViews.setTextViewText(R.id.widget_textview, name);
ComponentName thisWidget = new ComponentName(this, HelloWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);

即使“名称”是静态的(例如 String name="Something"),第一次安装时仍不会加载该文本视图。

【问题讨论】:

  • 我在这个问题上开了一个赏金,因为我在几周内没有取得任何进展。
  • 为什么你从来没有在你的更新函数中调用 super.onUpdate()?
  • 我称之为,我只是不知何故从代码中错过了它。我尝试将它作为 onUpdate 函数的第一行和最后一行: super.onUpdate(context, appWidgetManager, appWidgetIds);
  • 好的,抱歉。我更仔细地阅读了您的解决方案。我不明白的是你所说的“更新服务”循环。您的服务到底在做什么?看,我的小部件中不需要服务。我是否应该设置一个计时器来定期检查小部件是否需要刷新?

标签: android widget sharedpreferences


【解决方案1】:

每当您通过“new RemoteViews”创建新实例时,请尝试使用单击侦听器更新 RemoteViews。在某些情况下,RemoteViews 可能是从 XML 中新加载的,因此需要重新分配点击侦听器。

【讨论】:

  • 我发现每次更改小部件时远程视图都需要传递待处理的意图。否则它会在更新时被删除
【解决方案2】:

我的 UpdateService.java 只是获取设置的首选项并自定义小部件的外观,所以我认为这可能与我的问题无关。

它可能是相关的,因为您可以使用它来“刷新”待处理的意图。我的 appwidget 中有一个类似的问题,即图像按钮在随机运行时间(小时)后停止响应点击。

我找到了这个帖子: AppWidget Button onClick stops working

还有这句话:

待处理的意图在每次使用后被“烧毁”。您需要重新设置它。或者等待小部件刷新,然后它也会发生,但这可能不是所需的方式。

鉴于小部件更新时间通常设置为数小时或数天(我的是 86400000 毫秒),以防止手机每隔几分钟就停止挂起,您的小部件不会经常运行 onUpdate。在更新服务中设置挂起的 Intent ALSO 可能会阻止您描述的问题。每次更新服务运行时都会重新创建挂起的 Intent。

我今天已将这个可能的修复添加到我的 appwidget 中,我必须等待,看看修复是否真的有效,但到目前为止一切都很好。

我在刷新每个小部件的更新服务循环中添加了以下代码:

for (int i=0; i<appWidgetIds.length; i++)
{
  appWidgetId=appWidgetIds[i];

  /* other stuff to do */

  RemoteViews views=new RemoteViews(context.getPackageName(), R.layout.example_appwidget);

  /* here you "refresh" the pending intent for the button */
  Intent clickintent=new Intent("net.example.appwidget.ACTION_WIDGET_CLICK");
  PendingIntent pendingIntentClick=PendingIntent.getBroadcast(context, 0, clickintent, 0);
  views.setOnClickPendingIntent(R.id.example_appwidget_button, pendingIntentClick);
  appWidgetManager.updateAppWidget(appWidgetId, views);

  /* then tell the widget manager to update */
  appWidgetManager.updateAppWidget(appWidgetId, views);
}

【讨论】:

  • 经过一天的测试,我可以验证我上面建议的修复确实有效。我不确定这是否应该被视为一种解决方法,因为我不知道问题是由错误还是由“功能”引起的。
  • 我已经使用上述解决方案一个多月了,在安卓市场上的应用程序中也是如此,这对我来说是一个令人满意的解决方案。
  • “刷新”是什么意思(哪一行专门刷新它)?你为什么写了两次appWidgetManager.updateAppWidget(appWidgetId, views);
【解决方案3】:

问题是你不能对一个小部件进行部分更新,你必须设置所有的小部件特性,例如每次推送一个新的remoteView时设置的PendingIntent。 (部分更新仅适用于 API14 及更高版本...)。

您的小部件丢失其pendingIntents 的原因是android 系统保存了remoteView,并用它重建您的小部件,以防它重置小部件(内存不足、正在使用的TaskManager/taskKiller 等...),因此,您必须在 updateService 中的 remoteView 中设置小部件的所有更新代码。否则,它只是不会再次设置pendingIntents。

所以只需将设置pendingIntents的代码添加到服务中,您的问题就会得到解决=]

【讨论】:

    【解决方案4】:

    我认为 PendingIntents 可能需要一个标志传递给他们,也许尝试改变:

    PendingIntent.getActivity(context, REQUEST_CODE, configIntent, 0);
    

    到:

    PendingIntent.getActivity(context, REQUEST_CODE, configIntent, PendingIntent.FLAG_UPDATE_CURRENT);
    

    来自 PendingIntent documentation,我认为代码“0”未定义。在这种情况下,FLAG_UPDATE_CURRENT 效果最好,因为您可能希望每次单击按钮时都更新 Intent。

    【讨论】:

      【解决方案5】:

      鉴于您提供的所有信息,我认为您的更新方法在更改首选项时未正确触发。

      我希望经过这么多测试,您已经验证了您的清单文件包含:

          <intent-filter>
              <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
          </intent-filter>
      

      您确认onUpdate 曾经运行过吗?在我看来,如果在不卸载的情况下重新安装应用程序可以解决您的问题,那可能是因为它会强制进行更新调用。

      仔细检查后发现,ScanPlayGames 有一点:official documentation's example 使用了 super.onUpdate()。请注意,它在方法结束时使用它,但在 Internet 状态下的几个 examples 最好在方法开始时使用它。

      【讨论】:

      • 1.是的,Manifest 包含该行。 2. OnUpdate 必须运行,因为图像视图在小部件中工作。或者你是什么意思? 3.我的代码中有super.onUpdate(),我只是从这里漏掉了。
      • 关于 2.,在这种情况下,我倾向于在几乎每一行都记录消息,以防万一,如果可能的话,也会进入代码。我推断其他东西可能会使图像正常工作。如果我是你,我仍然会登录。除此之外,我很茫然。希望你能解决!
      【解决方案6】:

      我已经有这个问题很长时间了。我的小部件有按钮@(onUpdate)。该小部件具有更新服务。发生某些事情时,小部件上的按钮会停止工作,例如:更改字体等。

      当我重新安装应用程序时,该按钮再次起作用。最后,我意识到我从未在我的 Service 类中调用过 onUpdate。

      从服务类调用 onUpdate 解决了这个问题。

      【讨论】:

        【解决方案7】:

        如果有人仍然遇到此问题,请尝试在您的 AppWidgetProviderInfo 中设置属性 android:updatePeriodMillis; 操作系统可以出于各种原因终止待处理的意图,并且您的按钮可能会停止工作。当您设置此属性时,您是在告诉 Android 何时应该调用 AppWidgetProvider 中的 onUpdate 方法,因此将重新创建所有待处理的 Intent。

        <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
            ...
            android:updatePeriodMillis="3600000">
        </appwidget-provider>
        

        【讨论】:

        • 欢迎来到 StackOverflow!请提供带有参考资料的具体答案。如果您有建议或需要澄清,请发表评论。我们有关于How to write a good answer 的指导方针。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多