在 AppWidgetProvider 中注册 ContentObserver 以更新 AppWidget 不起作用

Registering a ContentObserver in AppWidgetProvider to update AppWidget is not working

我有一个来自主应用程序的 ContentProvider。内容将与消费者应用程序共享。这个消费者应用程序有一个应用程序小部件。我已经在其 Activity 中测试了此消费者应用程序的 ContentProviderContentObserver 并且一切正常(这意味着 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的变化。观察者注册在 AppWidgetProvideronEnabledonDisabled 部分。一旦观察者注意到 ContentProvider 发生变化,它会要求 AppWidgetProvider 更新自身,从而调用 onDataSetChanged 并获取新的数据列表。

但是,永远不会调用观察者。它在这里没有按预期工作的原因可能是什么? (这不可能是因为缺少权限,因为消费者应用程序的 Activity 部分能够很好地获取数据。)

What could be the reason it's not working as expected here?

AppWidgetProviderBroadcastReceiver 的子类。您的 AppWidgetProvider 实例将(希望)存活很短的时间,最好以毫秒为单位。基本上,您得到一个 onUpdate() 调用(或其他回调),然后该实例被丢弃。下一个回调获取一个新实例。

因此,在 AppWidgetProvider 中做任何需要它存在一段时间的事情都是注定要失败的。

考虑到 Android 的现代版本,最可能的解决方案是采用更多的推送解决方案。请记住,您的任何代码都可以更新应用小部件的 RemoteViews,只需使用 AppWidgetManager。因此,一些已经 运行 并且知道数据更新的代码需要推送一个新的 RemoteViews,而不是期望您的 AppWidgetProvider 能够对变化做出反应。