应用程序小部件被 Google 声音搜索劫持?
App Widget getting hijacked by Google Sound Search?
所以我在我编写的应用程序小部件中看到了一些奇怪的行为。
小部件本身非常简单 - 它从持久存储中读取一些值,然后将它们显示为文本。布局中没有什么特别的 - 只是一个 FrameLayout
根元素和一些 Linear Layout
和 TextView
个子元素。
小部件有一个简单的配置 activity 与之关联。
奇怪的是,在用户关闭配置 activity 后,小部件最初会显示 "Problem Loading Widget",然后在几秒钟后显示 "Google Sound Search" 按钮(并单击按钮上确实会启动 Google 声音搜索)。然后,再过几秒钟,它终于显示了预期的显示。
我现在离开我的代码,所以我必须等到今晚才能看到 post 代码片段。但是,与此同时,任何人都可以提供一些见解来了解这种事情是如何发生的吗?有没有其他人经历过这个?另一个小部件 "hijack" 怎么可能是我的?
谢谢,
-罗恩
以下是部分截图:
好的,我明白了。在这里张贴以防其他人遇到这个问题。我认为 Android 开发人员文档在这里有点误导。
问题是在我的配置 Activity 中,我在最后有这段代码:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
documentation provided by google 建议使用额外的 EXTRA_APPWIDGET_ID 提供意图。
但是,同一个文档说您必须通过创建 RemoteView
并像这样调用 AppWidgetManager.updateAppWidget()
来更新小部件的视图:
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
我不喜欢将表示逻辑同时放在配置 activity 和小部件 class 中的想法,因此我决定在配置结束时广播一个意图 activity 告诉小部件重绘自己。这就是为什么我在 activity 末尾有 setResult()
和 sendBroadcast()
。文档进一步说明 onUpdate()
回调在使用配置 activity 时不会被调用。所以这似乎是必要的。我将 ACTION_APPWIDGET_UPDATE
和 EXTRA_APPWIDGET_IDS
添加到意图中,以便它会触发 onUpdate()
方法。此 SO answer 推荐了这种做法(尽管未包含在 activity 结果意图中 - 但我尝试将两者分开但没有效果)。
现在我不确定 "Google Sound Search" 小部件是如何进入其中的,我也不完全理解意图如何相互作用以产生观察到的结果的机制。但是,一旦我将上面的代码替换为文档中所述的代码,小部件就会正确更新。
Intent resultIntent = new Intent();
resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultIntent);
finish();
这似乎与文档的声明相矛盾,即配置 activity 必须更新小部件的视图。只需提供如下配置 activity 结果即可触发小部件中的 onUpdate()
方法,从而允许小部件重绘自身。我在模拟器 运行 API 23 以及三星设备 运行 三星的 android flavor.
上确认了该行为
你的小部件有几个问题,所有问题都有答案(虽然你没有 post 任何代码,所以我的一些陈述是基于假设的):
"Problem loading widget":这是 Android 在小部件初始化和布局更新之前使用的默认视图。只需将以下行添加到您的小部件 xml 配置(以显示加载消息而不是问题消息):
android:initialLayout="@layout/my_cool_widget_loading_message"
如果小部件显示错误的布局,那么您可能在小部件的 onReceive 方法中遇到了问题。 onReceive 会为所有小部件调用,无论广播是否针对该特定小部件。 Android 的 AppWidgetProvider 通过 appwidget Id 过滤广播并分派给其他方法(如 onUpdate)。
另请参阅:https://developer.android.com/reference/android/appwidget/AppWidgetProvider.html#onReceive(android.content.Context、android.content.Intent)。
如果您重写 onReceive(我假设您这样做),您需要调用 super.onReceive(Context, Intent) 以确保您的其他方法不会收到针对其他小部件的调用。
现在进行小部件的配置。如果您遵循 Google 文档,那么它会很好地工作。我要做的唯一改进是您引用的我的其他答案所建议的 ()。这不会发出两个广播。 setResult()/finish() 部分只终止配置 Activity 并让 Android 知道是否实际添加小部件(取决于结果是 RESULT_CANCELED 还是 RESULT_OK.
从您自己的回答中我可以看出为什么您的代码无法运行。代码是:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
首先不需要两次添加appWidgetId,使用AppWidgetManager.EXTRA_APPWIDGET_IDS版本就可以了。其次,您使用与 return 相同的 Intent 作为 Activity 的结果。 AFAIK 没有记录当您对该 Intent 设置操作时会发生什么,但我对 Android 小部件的经验是您需要严格遵守文档,否则您最终会遇到奇怪的问题(比如您遇到的问题).所以请使用两个不同的 Intent。
Activity 结果:
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
广播:
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidget.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
sendBroadcast(intent);
所以我在我编写的应用程序小部件中看到了一些奇怪的行为。
小部件本身非常简单 - 它从持久存储中读取一些值,然后将它们显示为文本。布局中没有什么特别的 - 只是一个 FrameLayout
根元素和一些 Linear Layout
和 TextView
个子元素。
小部件有一个简单的配置 activity 与之关联。
奇怪的是,在用户关闭配置 activity 后,小部件最初会显示 "Problem Loading Widget",然后在几秒钟后显示 "Google Sound Search" 按钮(并单击按钮上确实会启动 Google 声音搜索)。然后,再过几秒钟,它终于显示了预期的显示。
我现在离开我的代码,所以我必须等到今晚才能看到 post 代码片段。但是,与此同时,任何人都可以提供一些见解来了解这种事情是如何发生的吗?有没有其他人经历过这个?另一个小部件 "hijack" 怎么可能是我的?
谢谢, -罗恩
以下是部分截图:
好的,我明白了。在这里张贴以防其他人遇到这个问题。我认为 Android 开发人员文档在这里有点误导。
问题是在我的配置 Activity 中,我在最后有这段代码:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
documentation provided by google 建议使用额外的 EXTRA_APPWIDGET_ID 提供意图。
但是,同一个文档说您必须通过创建 RemoteView
并像这样调用 AppWidgetManager.updateAppWidget()
来更新小部件的视图:
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.example_appwidget);
appWidgetManager.updateAppWidget(mAppWidgetId, views);
我不喜欢将表示逻辑同时放在配置 activity 和小部件 class 中的想法,因此我决定在配置结束时广播一个意图 activity 告诉小部件重绘自己。这就是为什么我在 activity 末尾有 setResult()
和 sendBroadcast()
。文档进一步说明 onUpdate()
回调在使用配置 activity 时不会被调用。所以这似乎是必要的。我将 ACTION_APPWIDGET_UPDATE
和 EXTRA_APPWIDGET_IDS
添加到意图中,以便它会触发 onUpdate()
方法。此 SO answer 推荐了这种做法(尽管未包含在 activity 结果意图中 - 但我尝试将两者分开但没有效果)。
现在我不确定 "Google Sound Search" 小部件是如何进入其中的,我也不完全理解意图如何相互作用以产生观察到的结果的机制。但是,一旦我将上面的代码替换为文档中所述的代码,小部件就会正确更新。
Intent resultIntent = new Intent();
resultIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultIntent);
finish();
这似乎与文档的声明相矛盾,即配置 activity 必须更新小部件的视图。只需提供如下配置 activity 结果即可触发小部件中的 onUpdate()
方法,从而允许小部件重绘自身。我在模拟器 运行 API 23 以及三星设备 运行 三星的 android flavor.
你的小部件有几个问题,所有问题都有答案(虽然你没有 post 任何代码,所以我的一些陈述是基于假设的):
"Problem loading widget":这是 Android 在小部件初始化和布局更新之前使用的默认视图。只需将以下行添加到您的小部件 xml 配置(以显示加载消息而不是问题消息):
android:initialLayout="@layout/my_cool_widget_loading_message"
如果小部件显示错误的布局,那么您可能在小部件的 onReceive 方法中遇到了问题。 onReceive 会为所有小部件调用,无论广播是否针对该特定小部件。 Android 的 AppWidgetProvider 通过 appwidget Id 过滤广播并分派给其他方法(如 onUpdate)。
另请参阅:https://developer.android.com/reference/android/appwidget/AppWidgetProvider.html#onReceive(android.content.Context、android.content.Intent)。
如果您重写 onReceive(我假设您这样做),您需要调用 super.onReceive(Context, Intent) 以确保您的其他方法不会收到针对其他小部件的调用。
现在进行小部件的配置。如果您遵循 Google 文档,那么它会很好地工作。我要做的唯一改进是您引用的我的其他答案所建议的 ()。这不会发出两个广播。 setResult()/finish() 部分只终止配置 Activity 并让 Android 知道是否实际添加小部件(取决于结果是 RESULT_CANCELED 还是 RESULT_OK.
从您自己的回答中我可以看出为什么您的代码无法运行。代码是:
Intent intent = new Intent();
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, intent);
sendBroadcast(intent);
finish();
首先不需要两次添加appWidgetId,使用AppWidgetManager.EXTRA_APPWIDGET_IDS版本就可以了。其次,您使用与 return 相同的 Intent 作为 Activity 的结果。 AFAIK 没有记录当您对该 Intent 设置操作时会发生什么,但我对 Android 小部件的经验是您需要严格遵守文档,否则您最终会遇到奇怪的问题(比如您遇到的问题).所以请使用两个不同的 Intent。 Activity 结果:
Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
广播:
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidget.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {mAppWidgetId});
sendBroadcast(intent);