在 AppWidgetProvider 中注册 ContentObserver 以更新 AppWidget 不起作用
Registering a ContentObserver in AppWidgetProvider to update AppWidget is not working
我有一个来自主应用程序的 ContentProvider
。内容将与消费者应用程序共享。这个消费者应用程序有一个应用程序小部件。我已经在其 Activity 中测试了此消费者应用程序的 ContentProvider
和 ContentObserver
并且一切正常(这意味着 Activity 的 RecyclerView
会在更新时更新从主应用程序触发对数据库的更改)。但是,在我的 AppWidgetProvider
中注册 ContentObserver
没有按预期工作。
我的AppWidgetProvider
有以下代码。
class StackWidgetProvider : AppWidgetProvider() {
override fun onEnabled(context: Context) {
Timber.i("Enabled")
if (favoriteUserProviderObserver == null) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val componentName = ComponentName(context, StackWidgetProvider::class.java)
favoriteUserProviderObserver = FavoriteUserProviderObserver(appWidgetManager, componentName).let {
context.contentResolver.registerContentObserver(CONTENT_URI, true, it)
it
}
}
}
override fun onDisabled(context: Context) {
Timber.i("Disabled")
favoriteUserProviderObserver?.let {
context.contentResolver.unregisterContentObserver(it)
}
favoriteUserProviderObserver = null
}
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
....
companion object {
private var favoriteUserProviderObserver: FavoriteUserProviderObserver? = null
private fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val intent = Intent(context, StackWidgetService::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
data = toUri(Intent.URI_INTENT_SCHEME).toUri()
}
val views = RemoteViews(context.packageName, R.layout.widget_favorite_user_stack).apply {
setRemoteAdapter(R.id.widget_favorite_user_stack_view, intent)
setEmptyView(R.id.widget_favorite_user_stack_view, R.id.widget_favorite_user_empty)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
我创建了一个简单的自定义 ContentObserver
class,如下所示。
class FavoriteUserProviderObserver(
private val appWidgetManager: AppWidgetManager,
private val componentName: ComponentName
) : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
Timber.i("Provider observer triggered")
appWidgetManager.notifyAppWidgetViewDataChanged(
appWidgetManager.getAppWidgetIds(componentName), R.id.widget_favorite_user_stack_view
)
}
}
上面的观察者 class 永远不会被触发(即使我在我的主应用程序中更改数据)。为进一步清楚起见,这是我的 RemoteViewsService
及其工厂的代码。
class StackWidgetService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory =
StackRemoteViewsFactory(this.applicationContext)
}
class StackRemoteViewsFactory(private val context: Context) :
RemoteViewsService.RemoteViewsFactory {
private var widgetItems = listOf<UserProfileSummary>()
private lateinit var repository: FavoriteUserRepository
override fun onCreate() {
repository = FavoriteUserRepository(
FavoriteUserDataSource(context.contentResolver),
Dispatchers.IO
)
}
override fun onDataSetChanged() {
GlobalScope.launch {
widgetItems = repository.favoriteUsers().toList() // Tested; working on the Activity scope of the consumer app
Timber.i(widgetItems.toString())
}
}
override fun getViewAt(position: Int): RemoteViews =
RemoteViews(context.packageName, R.layout.widget_favorite_user_item).apply {
setTextViewText(R.id.widget_favorite_user_item_text, widgetItems[position].username)
}
override fun getLoadingView(): RemoteViews? = null
override fun getItemId(position: Int): Long = 0
override fun hasStableIds(): Boolean = false
override fun getCount(): Int {
return widgetItems.size
}
override fun getViewTypeCount(): Int = 1
override fun onDestroy() {}
}
所以逻辑是让ContentObserver
去观察ContentProvider
的变化。观察者注册在 AppWidgetProvider
的 onEnabled
和 onDisabled
部分。一旦观察者注意到 ContentProvider
发生变化,它会要求 AppWidgetProvider
更新自身,从而调用 onDataSetChanged
并获取新的数据列表。
但是,永远不会调用观察者。它在这里没有按预期工作的原因可能是什么? (这不可能是因为缺少权限,因为消费者应用程序的 Activity 部分能够很好地获取数据。)
What could be the reason it's not working as expected here?
AppWidgetProvider
是 BroadcastReceiver
的子类。您的 AppWidgetProvider
实例将(希望)存活很短的时间,最好以毫秒为单位。基本上,您得到一个 onUpdate()
调用(或其他回调),然后该实例被丢弃。下一个回调获取一个新实例。
因此,在 AppWidgetProvider
中做任何需要它存在一段时间的事情都是注定要失败的。
考虑到 Android 的现代版本,最可能的解决方案是采用更多的推送解决方案。请记住,您的任何代码都可以更新应用小部件的 RemoteViews
,只需使用 AppWidgetManager
。因此,一些已经 运行 并且知道数据更新的代码需要推送一个新的 RemoteViews
,而不是期望您的 AppWidgetProvider
能够对变化做出反应。
我有一个来自主应用程序的 ContentProvider
。内容将与消费者应用程序共享。这个消费者应用程序有一个应用程序小部件。我已经在其 Activity 中测试了此消费者应用程序的 ContentProvider
和 ContentObserver
并且一切正常(这意味着 Activity 的 RecyclerView
会在更新时更新从主应用程序触发对数据库的更改)。但是,在我的 AppWidgetProvider
中注册 ContentObserver
没有按预期工作。
我的AppWidgetProvider
有以下代码。
class StackWidgetProvider : AppWidgetProvider() {
override fun onEnabled(context: Context) {
Timber.i("Enabled")
if (favoriteUserProviderObserver == null) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val componentName = ComponentName(context, StackWidgetProvider::class.java)
favoriteUserProviderObserver = FavoriteUserProviderObserver(appWidgetManager, componentName).let {
context.contentResolver.registerContentObserver(CONTENT_URI, true, it)
it
}
}
}
override fun onDisabled(context: Context) {
Timber.i("Disabled")
favoriteUserProviderObserver?.let {
context.contentResolver.unregisterContentObserver(it)
}
favoriteUserProviderObserver = null
}
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray
) {
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
....
companion object {
private var favoriteUserProviderObserver: FavoriteUserProviderObserver? = null
private fun updateAppWidget(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetId: Int
) {
val intent = Intent(context, StackWidgetService::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
data = toUri(Intent.URI_INTENT_SCHEME).toUri()
}
val views = RemoteViews(context.packageName, R.layout.widget_favorite_user_stack).apply {
setRemoteAdapter(R.id.widget_favorite_user_stack_view, intent)
setEmptyView(R.id.widget_favorite_user_stack_view, R.id.widget_favorite_user_empty)
}
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
}
我创建了一个简单的自定义 ContentObserver
class,如下所示。
class FavoriteUserProviderObserver(
private val appWidgetManager: AppWidgetManager,
private val componentName: ComponentName
) : ContentObserver(null) {
override fun onChange(selfChange: Boolean) {
Timber.i("Provider observer triggered")
appWidgetManager.notifyAppWidgetViewDataChanged(
appWidgetManager.getAppWidgetIds(componentName), R.id.widget_favorite_user_stack_view
)
}
}
上面的观察者 class 永远不会被触发(即使我在我的主应用程序中更改数据)。为进一步清楚起见,这是我的 RemoteViewsService
及其工厂的代码。
class StackWidgetService : RemoteViewsService() {
override fun onGetViewFactory(intent: Intent): RemoteViewsFactory =
StackRemoteViewsFactory(this.applicationContext)
}
class StackRemoteViewsFactory(private val context: Context) :
RemoteViewsService.RemoteViewsFactory {
private var widgetItems = listOf<UserProfileSummary>()
private lateinit var repository: FavoriteUserRepository
override fun onCreate() {
repository = FavoriteUserRepository(
FavoriteUserDataSource(context.contentResolver),
Dispatchers.IO
)
}
override fun onDataSetChanged() {
GlobalScope.launch {
widgetItems = repository.favoriteUsers().toList() // Tested; working on the Activity scope of the consumer app
Timber.i(widgetItems.toString())
}
}
override fun getViewAt(position: Int): RemoteViews =
RemoteViews(context.packageName, R.layout.widget_favorite_user_item).apply {
setTextViewText(R.id.widget_favorite_user_item_text, widgetItems[position].username)
}
override fun getLoadingView(): RemoteViews? = null
override fun getItemId(position: Int): Long = 0
override fun hasStableIds(): Boolean = false
override fun getCount(): Int {
return widgetItems.size
}
override fun getViewTypeCount(): Int = 1
override fun onDestroy() {}
}
所以逻辑是让ContentObserver
去观察ContentProvider
的变化。观察者注册在 AppWidgetProvider
的 onEnabled
和 onDisabled
部分。一旦观察者注意到 ContentProvider
发生变化,它会要求 AppWidgetProvider
更新自身,从而调用 onDataSetChanged
并获取新的数据列表。
但是,永远不会调用观察者。它在这里没有按预期工作的原因可能是什么? (这不可能是因为缺少权限,因为消费者应用程序的 Activity 部分能够很好地获取数据。)
What could be the reason it's not working as expected here?
AppWidgetProvider
是 BroadcastReceiver
的子类。您的 AppWidgetProvider
实例将(希望)存活很短的时间,最好以毫秒为单位。基本上,您得到一个 onUpdate()
调用(或其他回调),然后该实例被丢弃。下一个回调获取一个新实例。
因此,在 AppWidgetProvider
中做任何需要它存在一段时间的事情都是注定要失败的。
考虑到 Android 的现代版本,最可能的解决方案是采用更多的推送解决方案。请记住,您的任何代码都可以更新应用小部件的 RemoteViews
,只需使用 AppWidgetManager
。因此,一些已经 运行 并且知道数据更新的代码需要推送一个新的 RemoteViews
,而不是期望您的 AppWidgetProvider
能够对变化做出反应。