安卓小部件按钮停止工作

13

我有一个带有按钮的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);

    }
}

编辑:这样如何:

当我在我的同事的中兴 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);
即使"name"是静态的(例如String name="Something"),那个textview仍然不会在第一次安装时加载。

我在这个问题上开了一个悬赏,因为我已经几周没有取得任何进展了。 - erdomester
3
为什么你在update函数中从未调用过super.onUpdate()? - Delete
我之前确实在代码中忘记调用了它。我尝试将其作为 onUpdate 函数的第一行和最后一行调用:super.onUpdate(context, appWidgetManager, appWidgetIds); - erdomester
好的,抱歉。我仔细阅读了您的解决方案。我不理解的是您所谓的“更新服务”循环是什么。您的服务到底在做什么?您看,我不需要在我的小部件中使用服务。我应该只是设置一个定时器来定期检查小部件是否需要刷新吗? - KairisCharm
7个回答

6
我的UpdateService.java只是获取设置的偏好和自定义小部件的外观,所以我认为它不可能与我的问题有关。但是这是可能相关的,因为你可以使用它来“刷新”待处理意图。我的应用程序小部件存在类似的问题,即在一些随机运行时间(几个小时)后,图像按钮停止响应点击事件。
我在这个线程中找到了解决方法: AppWidget Button onClick stops working 并引用了以下内容:
“每次使用后,待处理意图都会被“烧毁”。你需要再次设置它。或者等待小部件被刷新,然后它也会发生,但这可能不是期望的方式。”
考虑到小部件更新时间通常设置为数小时或数天(我的是86400000毫秒),以防止手机每隔几分钟就进入挂起状态,因此你的小部件不经常运行onUpdate。在UpdateService中也设置待处理意图可能会防止你描述的问题。每次UpdateService运行时,待处理意图都会被重新创建。
今天我已经在我的应用程序小部件中添加了这个可能的修复方法,我必须等待并看看修复是否真正有效,但目前为止效果很好。
我在UpdateService的循环中添加了以下代码,用于刷新每个小部件:
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);
}

经过一天的测试,我可以确认我上面建议的修复确实有效。我不确定这是否应该被视为一种解决方法,因为我不知道问题是由错误还是“特性”引起的。 - aseq
我已经使用上述解决方案超过一个月了,还在安卓市场上提供的应用程序中使用,对我来说是一个令人满意的解决方案。 - aseq
请帮帮我!:http://stackoverflow.com/questions/29598491/widget-stops-responding-to-clicks-widget-freezes?lq=1 - Muhammad Ali
你说的“刷新”是指哪一行代码实现了刷新功能?为什么要写两次 appWidgetManager.updateAppWidget(appWidgetId, views); - fsljfke

6

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


我发现远程视图在每次更改小部件时需要传递挂起意图,否则会在更新时被删除。 - Konstantin Pribluda

2

问题在于,您无法对小部件进行部分更新,每次推送新的remoteView时必须设置所有小部件功能,例如PendingIntent集合。 (仅适用于API14及更高版本的Partiall更新...)。

您的小部件失去pendingIntents的原因是Android系统保存了remoteView,并在重置小部件(内存短缺,TaskManager / taskKiller正在使用等)时重新构建它,因此您必须将小部件的所有更新代码设置为远程视图中的updateService,否则它就不会再次设置pendingIntents。

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


0
如果有人仍然遇到这个问题,请在您的AppWidgetProviderInfo中设置属性android:updatePeriodMillis; 操作系统可能因为各种原因而终止挂起的意图,导致您的按钮停止工作。当您设置此属性时,您告诉Android应该在何时调用AppWidgetProvider中的onUpdate方法,因此所有挂起的意图都将被重新创建。
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    android:updatePeriodMillis="3600000">
</appwidget-provider>

欢迎来到StackOverflow!请提供具有参考依据的具体答案。如果您有建议或需要澄清,请留下评论。我们有关于如何撰写好答案的指南。 - Joseph Cho

0
根据您提供的所有信息,我认为当首选项更改时,您的更新方法未正确触发。
我期望在进行了如此多的测试后,您已经验证了您的清单文件包含:
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>

你确认过 onUpdate 是否被执行了吗?如果重新安装应用程序而不是卸载解决了你的问题,那么可能是因为它强制调用了更新。

经过仔细检查,发现 ScanPlayGames 有一点:官方文档示例 使用了 super.onUpdate()。请注意,它在方法末尾使用了它,但是互联网上的几个 示例 声称最好在方法开头使用它。


  1. 是的,清单文件包含那行代码。
  2. 因为小部件中的图像视图正在工作,所以必须运行OnUpdate。或者你是什么意思?
  3. 我的代码中有super.onUpdate(),我只是在这里漏掉了它。
- erdomester
关于第二点,在这种情况下我倾向于几乎每行都记录消息,以确保一切顺利,并尽可能地进入代码。我认为其他因素可能会使图像正常工作。如果我是你,我仍然会记录日志。除此之外,我无能为力。希望你能解决它! - Kheldar

0

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

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

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


0

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

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

to:

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

从PendingIntent 文档中,我认为代码'0'未定义。在这种情况下,FLAG_UPDATE_CURRENT可能是最好的选择,因为您可能希望每次单击按钮时更新Intent。

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