Регистрация ContentObserver в AppWidgetProvider для обновления AppWidget не работает - PullRequest
1 голос
/ 07 августа 2020

У меня ContentProvider из основного приложения. Контент будет доступен для пользовательского приложения. В этом потребительском приложении есть виджет приложения. Я протестировал ContentProvider и ContentObserver этого потребительского приложения в его Activity, и все в порядке (это означает, что RecyclerView Activity обновляется всякий раз, когда обновление из основного приложения вызывает изменения в базе данных). Однако регистрация ContentObserver внутри моего AppWidgetProvider не работает должным образом.

Мой 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 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
        )
    }
}

Указанный выше класс наблюдателя никогда не запускается (даже когда я изменяю данные в своем основном приложении). Для большей ясности вот код для моего 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() {}
}

Итак, logi c - это запрос ContentObserver на наблюдение изменений в ContentProvider. Наблюдатель зарегистрирован в onEnabled и onDisabled части AppWidgetProvider. Как только наблюдатель заметит изменение в ContentProvider, он попросит AppWidgetProvider обновить себя, таким образом вызывая onDataSetChanged и получая новый список данных.

Однако наблюдатель никогда не вызывается. Что может быть причиной того, что здесь он работает не так, как ожидалось? (Это не может быть из-за отсутствия разрешения, потому что часть Activity в приложении-потребителе может без проблем получать данные.)

1 Ответ

1 голос
/ 07 августа 2020

В чем может быть причина, по которой он здесь не работает должным образом?

AppWidgetProvider является подклассом BroadcastReceiver. Ваш экземпляр AppWidgetProvider будет жить (надеюсь) очень короткое время, лучше всего измеренное в миллисекундах. По сути, вы получаете один вызов onUpdate() (или другой обратный вызов), и этот экземпляр отбрасывается. Следующий обратный вызов получает новый экземпляр.

В результате выполнение чего-либо в AppWidgetProvider, которое требует, чтобы он существовал в течение определенного периода времени, обречен.

Наиболее вероятное решение, принимая во внимание современные версии Android, следует принять решение pu sh. Имейте в виду, что любой код может обновить RemoteViews для виджета приложения, просто работая с AppWidgetManager. Итак, некоторый код, который уже запущен и знает об обновлениях данных, должен преобразовать sh новый RemoteViews вместо того, чтобы ожидать, что ваш AppWidgetProvider сможет реагировать на изменения.

...